GTest unit testing framework

Currently covers only small set of functionality.
This commit is contained in:
Sergey Sharybin 2014-06-18 22:49:17 +10:00 committed by Campbell Barton
parent 47ec0394ca
commit 306cbb82ec
88 changed files with 36146 additions and 286 deletions

View File

@ -109,6 +109,7 @@ enable_testing()
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin CACHE INTERNAL "" FORCE)
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib CACHE INTERNAL "" FORCE)
set(TESTS_OUTPUT_DIR ${EXECUTABLE_OUTPUT_PATH}/tests CACHE INTERNAL "" FORCE)
#-----------------------------------------------------------------------------
# Set default config options
@ -300,6 +301,9 @@ if(CMAKE_COMPILER_IS_GNUCC)
mark_as_advanced(WITH_GCC_MUDFLAP)
endif()
# Unit testsing
option(WITH_GTESTS "Enable GTest unit testing" OFF)
if(APPLE)
cmake_minimum_required(VERSION 2.8.8)
cmake_policy(VERSION 2.8.8)
@ -2330,10 +2334,17 @@ endif()
#-----------------------------------------------------------------------------
# Libraries
if(WITH_TESTS)
include(GTestTesting)
endif()
if(WITH_BLENDER OR WITH_PLAYER)
add_subdirectory(source)
add_subdirectory(intern)
add_subdirectory(extern)
# source after intern and extern to gather all
# internal and external library information first, for test linking
add_subdirectory(source)
elseif(WITH_CYCLES_STANDALONE)
add_subdirectory(intern/cycles)
if(NOT WITH_SYSTEM_GLEW)

View File

@ -0,0 +1,47 @@
#=============================================================================
# Copyright 2014 Blender Foundation.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#
# Inspired on the Testing.cmake from Libmv
#
#=============================================================================
macro(BLENDER_SRC_GTEST NAME SRC EXTRA_LIBS)
if(WITH_TESTS)
get_property(_current_include_directories
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
PROPERTY INCLUDE_DIRECTORIES)
set(TEST_INC
${_current_include_directories}
${CMAKE_SOURCE_DIR}/tests/gtests
${CMAKE_SOURCE_DIR}/extern/libmv/third_party/glog/src
${CMAKE_SOURCE_DIR}/extern/libmv/third_party/gflags
${CMAKE_SOURCE_DIR}/extern/gtest/include
)
unset(_current_include_directories)
add_executable(${NAME}_test ${SRC})
target_link_libraries(${NAME}_test
${EXTRA_LIBS}
bf_testing_main
bf_intern_guardedalloc
extern_gtest
extern_glog)
set_target_properties(${NAME}_test PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${TESTS_OUTPUT_DIR}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${TESTS_OUTPUT_DIR}"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${TESTS_OUTPUT_DIR}"
INCLUDE_DIRECTORIES "${TEST_INC}")
add_test(${NAME}_test ${TESTS_OUTPUT_DIR}/${NAME}_test)
endif()
endmacro()
macro(BLENDER_TEST NAME EXTRA_LIBS)
BLENDER_SRC_GTEST("${NAME}" "${NAME}_test.cc" "${EXTRA_LIBS}")
endmacro()

View File

@ -413,6 +413,240 @@ macro(setup_liblinks
target_link_libraries(${target} ${PLATFORM_LINKLIBS} ${CMAKE_DL_LIBS})
endmacro()
macro(SETUP_BLENDER_SORTED_LIBS)
get_property(BLENDER_LINK_LIBS GLOBAL PROPERTY BLENDER_LINK_LIBS)
list(APPEND BLENDER_LINK_LIBS
bf_windowmanager
bf_render
)
if(WITH_MOD_FLUID)
list(APPEND BLENDER_LINK_LIBS bf_intern_elbeem)
endif()
if(WITH_CYCLES)
list(APPEND BLENDER_LINK_LIBS
cycles_render
cycles_bvh
cycles_device
cycles_kernel
cycles_util
cycles_subd)
if(WITH_CYCLES_OSL)
list(APPEND BLENDER_LINK_LIBS cycles_kernel_osl)
endif()
endif()
# Sort libraries
set(BLENDER_SORTED_LIBS
bf_windowmanager
bf_editor_space_api
bf_editor_space_action
bf_editor_space_buttons
bf_editor_space_console
bf_editor_space_file
bf_editor_space_graph
bf_editor_space_image
bf_editor_space_info
bf_editor_space_logic
bf_editor_space_nla
bf_editor_space_node
bf_editor_space_outliner
bf_editor_space_script
bf_editor_space_sequencer
bf_editor_space_text
bf_editor_space_time
bf_editor_space_userpref
bf_editor_space_view3d
bf_editor_space_clip
bf_editor_transform
bf_editor_util
bf_editor_uvedit
bf_editor_curve
bf_editor_gpencil
bf_editor_interface
bf_editor_mesh
bf_editor_metaball
bf_editor_object
bf_editor_armature
bf_editor_physics
bf_editor_render
bf_editor_screen
bf_editor_sculpt_paint
bf_editor_sound
bf_editor_animation
bf_editor_datafiles
bf_editor_mask
bf_editor_io
bf_render
bf_python
bf_python_ext
bf_python_mathutils
bf_python_bmesh
bf_freestyle
bf_ikplugin
bf_modifiers
bf_bmesh
bf_blenkernel
bf_nodes
bf_gpu
bf_blenloader
bf_imbuf
bf_blenlib
bf_intern_ghost
bf_intern_string
bf_avi
bf_imbuf_cineon
bf_imbuf_openexr
bf_imbuf_openimageio
bf_imbuf_dds
bf_collada
bf_intern_elbeem
bf_intern_memutil
bf_intern_guardedalloc
bf_intern_ctr
bf_intern_utfconv
ge_blen_routines
ge_converter
ge_phys_dummy
ge_phys_bullet
bf_intern_smoke
extern_minilzo
extern_lzma
extern_colamd
ge_logic_ketsji
extern_recastnavigation
ge_logic
ge_rasterizer
ge_oglrasterizer
ge_logic_expressions
ge_scenegraph
ge_logic_network
ge_logic_ngnetwork
ge_logic_loopbacknetwork
bf_intern_moto
extern_openjpeg
extern_redcode
ge_videotex
bf_rna
bf_dna
bf_blenfont
bf_intern_audaspace
bf_intern_mikktspace
bf_intern_dualcon
bf_intern_cycles
cycles_render
cycles_bvh
cycles_device
cycles_kernel
cycles_util
cycles_subd
bf_intern_raskter
bf_intern_opencolorio
extern_rangetree
extern_wcwidth
extern_libmv
extern_glog
)
if(WITH_COMPOSITOR)
# added for opencl compositor
list_insert_before(BLENDER_SORTED_LIBS "bf_blenkernel" "bf_compositor")
list_insert_after(BLENDER_SORTED_LIBS "bf_compositor" "bf_intern_opencl")
endif()
if(WITH_LIBMV)
list(APPEND BLENDER_SORTED_LIBS extern_ceres)
endif()
if(WITH_MOD_CLOTH_ELTOPO)
list(APPEND BLENDER_SORTED_LIBS extern_eltopo)
endif()
if(NOT WITH_SYSTEM_GLEW)
list(APPEND BLENDER_SORTED_LIBS extern_glew)
endif()
if(WITH_BINRELOC)
list(APPEND BLENDER_SORTED_LIBS extern_binreloc)
endif()
if(WITH_CXX_GUARDEDALLOC)
list(APPEND BLENDER_SORTED_LIBS bf_intern_guardedalloc_cpp)
endif()
if(WITH_IK_SOLVER)
list_insert_after(BLENDER_SORTED_LIBS "bf_intern_elbeem" "bf_intern_iksolver")
endif()
if(WITH_IK_ITASC)
list(APPEND BLENDER_SORTED_LIBS bf_intern_itasc)
endif()
if(WITH_CODEC_QUICKTIME)
list(APPEND BLENDER_SORTED_LIBS bf_quicktime)
endif()
if(WITH_INPUT_NDOF)
list(APPEND BLENDER_SORTED_LIBS bf_intern_ghostndof3dconnexion)
endif()
if(WITH_MOD_BOOLEAN)
list(APPEND BLENDER_SORTED_LIBS extern_carve)
endif()
if(WITH_GHOST_XDND)
list(APPEND BLENDER_SORTED_LIBS extern_xdnd)
endif()
if(WITH_CYCLES_OSL)
list_insert_after(BLENDER_SORTED_LIBS "cycles_kernel" "cycles_kernel_osl")
endif()
if(WITH_INTERNATIONAL)
list(APPEND BLENDER_SORTED_LIBS bf_intern_locale)
endif()
if(WITH_OPENNL)
list_insert_after(BLENDER_SORTED_LIBS "bf_render" "bf_intern_opennl")
endif()
if(WITH_BULLET)
list_insert_after(BLENDER_SORTED_LIBS "bf_blenkernel" "bf_intern_rigidbody")
endif()
if(WITH_BULLET AND NOT WITH_SYSTEM_BULLET)
list_insert_after(BLENDER_SORTED_LIBS "ge_logic_ngnetwork" "extern_bullet")
endif()
foreach(SORTLIB ${BLENDER_SORTED_LIBS})
set(REMLIB ${SORTLIB})
foreach(SEARCHLIB ${BLENDER_LINK_LIBS})
if(${SEARCHLIB} STREQUAL ${SORTLIB})
set(REMLIB "")
endif()
endforeach()
if(REMLIB)
# message(STATUS "Removing library ${REMLIB} from blender linking because: not configured")
list(APPEND REM_MSG ${REMLIB})
list(REMOVE_ITEM BLENDER_SORTED_LIBS ${REMLIB})
endif()
endforeach()
if(REM_MSG)
list(SORT REM_MSG)
message(STATUS "Blender Skipping: (${REM_MSG})")
endif()
unset(SEARCHLIB)
unset(SORTLIB)
unset(REMLIB)
unset(REM_MSG)
endmacro()
macro(TEST_SSE_SUPPORT
_sse_flags
_sse2_flags)

View File

@ -79,3 +79,7 @@ if(WITH_GHOST_XDND)
add_subdirectory(xdnd)
endif()
endif()
if(WITH_TESTS)
add_subdirectory(gtest)
endif()

64
extern/gtest/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,64 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# The Original Code is Copyright (C) 2014, Blender Foundation
# All rights reserved.
#
# Contributor(s): Sergey Sharybin
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
include
)
set(INC_SYS
)
set(SRC
src/gtest.cc
src/gtest-death-test.cc
src/gtest-filepath.cc
src/gtest-port.cc
src/gtest-printers.cc
src/gtest-test-part.cc
src/gtest-typed-test.cc
include/gtest/gtest-death-test.h
include/gtest/gtest.h
include/gtest/gtest-message.h
include/gtest/gtest-param-test.h
include/gtest/gtest_pred_impl.h
include/gtest/gtest-printers.h
include/gtest/gtest_prod.h
include/gtest/gtest-spi.h
include/gtest/gtest-test-part.h
include/gtest/gtest-typed-test.h
include/gtest/internal/gtest-death-test-internal.h
include/gtest/internal/gtest-filepath.h
include/gtest/internal/gtest-internal.h
include/gtest/internal/gtest-linked_ptr.h
include/gtest/internal/gtest-param-util-generated.h
include/gtest/internal/gtest-param-util.h
include/gtest/internal/gtest-port.h
include/gtest/internal/gtest-string.h
include/gtest/internal/gtest-tuple.h
include/gtest/internal/gtest-type-util.h
)
blender_add_lib(extern_gtest "${SRC}" "${INC}" "${INC_SYS}")

28
extern/gtest/LICENSE vendored Normal file
View File

@ -0,0 +1,28 @@
Copyright 2008, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

7
extern/gtest/README vendored Normal file
View File

@ -0,0 +1,7 @@
Project: Google C++ Testing Framework
URL: http://code.google.com/p/googletest
License: New BSD
Upstream version: 1.7.0
Local modifications:
None.

View File

@ -0,0 +1,294 @@
// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
//
// The Google C++ Testing 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.
#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
#include "gtest/internal/gtest-death-test-internal.h"
namespace testing {
// This flag controls the style of death tests. Valid values are "threadsafe",
// meaning that the death test child process will re-execute the test binary
// from the start, running only a single death test, or "fast",
// meaning that the child process will execute the test logic immediately
// after forking.
GTEST_DECLARE_string_(death_test_style);
#if GTEST_HAS_DEATH_TEST
namespace internal {
// Returns a Boolean value indicating whether the caller is currently
// executing in the context of the death test child process. Tools such as
// Valgrind heap checkers may need this to modify their behavior in death
// tests. IMPORTANT: This is an internal utility. Using it may break the
// implementation of death tests. User code MUST NOT use it.
GTEST_API_ bool InDeathTestChild();
} // namespace internal
// The following macros are useful for writing death tests.
// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is
// executed:
//
// 1. It generates a warning if there is more than one active
// thread. This is because it's safe to fork() or clone() only
// when there is a single thread.
//
// 2. The parent process clone()s a sub-process and runs the death
// test in it; the sub-process exits with code 0 at the end of the
// death test, if it hasn't exited already.
//
// 3. The parent process waits for the sub-process to terminate.
//
// 4. The parent process checks the exit code and error message of
// the sub-process.
//
// Examples:
//
// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number");
// for (int i = 0; i < 5; i++) {
// EXPECT_DEATH(server.ProcessRequest(i),
// "Invalid request .* in ProcessRequest()")
// << "Failed to die on request " << i;
// }
//
// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting");
//
// bool KilledBySIGHUP(int exit_code) {
// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP;
// }
//
// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
//
// On the regular expressions used in death tests:
//
// 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
// 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
// or POSIX extended regex syntax. For example, we don't support
// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and
// repetition count ("x{5,7}"), among others.
//
// Below is the syntax that we do support. We chose it to be a
// subset of both PCRE and POSIX extended regex, so it's easy to
// learn wherever you come from. In the following: 'A' denotes a
// literal character, period (.), or a single \\ escape sequence;
// 'x' and 'y' denote regular expressions; 'm' and 'n' are for
// natural numbers.
//
// c matches any literal character c
// \\d matches any decimal digit
// \\D matches any character that's not a decimal digit
// \\f matches \f
// \\n matches \n
// \\r matches \r
// \\s matches any ASCII whitespace, including \n
// \\S matches any character that's not a whitespace
// \\t matches \t
// \\v matches \v
// \\w matches any letter, _, or decimal digit
// \\W matches any character that \\w doesn't match
// \\c matches any literal character c, which must be a punctuation
// . matches any single character except \n
// A? matches 0 or 1 occurrences of A
// A* matches 0 or many occurrences of A
// A+ matches 1 or many occurrences of A
// ^ matches the beginning of a string (not that of each line)
// $ matches the end of a string (not that of each line)
// xy matches x followed by y
//
// If you accidentally use PCRE or POSIX extended regex features
// not implemented by us, you will get a run-time failure. In that
// case, please try to rewrite your regular expression within the
// above syntax.
//
// This implementation is *not* meant to be as highly tuned or robust
// as a compiled regex library, but should perform well enough for a
// death test, which already incurs significant overhead by launching
// a child process.
//
// Known caveats:
//
// A "threadsafe" style death test obtains the path to the test
// program from argv[0] and re-executes it in the sub-process. For
// simplicity, the current implementation doesn't search the PATH
// when launching the sub-process. This means that the user must
// invoke the test program via a path that contains at least one
// path separator (e.g. path/to/foo_test and
// /absolute/path/to/bar_test are fine, but foo_test is not). This
// is rarely a problem as people usually don't put the test binary
// directory in PATH.
//
// TODO(wan@google.com): make thread-safe death tests search the PATH.
// Asserts that a given statement causes the program to exit, with an
// 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_)
// 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_)
// 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)
// 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)
// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
// Tests that an exit code describes a normal exit with a given exit code.
class GTEST_API_ ExitedWithCode {
public:
explicit ExitedWithCode(int exit_code);
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
// Tests that an exit code describes an exit due to termination by a
// given signal.
class GTEST_API_ KilledBySignal {
public:
explicit KilledBySignal(int signum);
bool operator()(int exit_status) const;
private:
const int signum_;
};
# endif // !GTEST_OS_WINDOWS
// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode.
// The death testing framework causes this to have interesting semantics,
// since the sideeffects of the call are only visible in opt mode, and not
// in debug mode.
//
// In practice, this can be used to test functions that utilize the
// LOG(DFATAL) macro using the following style:
//
// int DieInDebugOr12(int* sideeffect) {
// if (sideeffect) {
// *sideeffect = 12;
// }
// LOG(DFATAL) << "death";
// return 12;
// }
//
// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) {
// int sideeffect = 0;
// // Only asserts in dbg.
// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
//
// #ifdef NDEBUG
// // opt-mode has sideeffect visible.
// EXPECT_EQ(12, sideeffect);
// #else
// // dbg-mode no visible sideeffect.
// EXPECT_EQ(0, sideeffect);
// #endif
// }
//
// This will assert that DieInDebugReturn12InOpt() crashes in debug
// mode, usually due to a DCHECK or LOG(DFATAL), but returns the
// appropriate fallback value (12 in this case) in opt mode. If you
// need to test that a function has appropriate side-effects in opt
// mode, include assertions against the side-effects. A general
// pattern for this is:
//
// EXPECT_DEBUG_DEATH({
// // Side-effects here will have an effect after this statement in
// // opt mode, but none in debug mode.
// EXPECT_EQ(12, DieInDebugOr12(&sideeffect));
// }, "death");
//
# ifdef NDEBUG
# define EXPECT_DEBUG_DEATH(statement, regex) \
GTEST_EXECUTE_STATEMENT_(statement, regex)
# define ASSERT_DEBUG_DEATH(statement, regex) \
GTEST_EXECUTE_STATEMENT_(statement, regex)
# else
# define EXPECT_DEBUG_DEATH(statement, regex) \
EXPECT_DEATH(statement, regex)
# define ASSERT_DEBUG_DEATH(statement, regex) \
ASSERT_DEATH(statement, regex)
# endif // NDEBUG for EXPECT_DEBUG_DEATH
#endif // GTEST_HAS_DEATH_TEST
// 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
// useful when you are combining death test assertions with normal test
// assertions in one test.
#if GTEST_HAS_DEATH_TEST
# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
EXPECT_DEATH(statement, regex)
# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
ASSERT_DEATH(statement, regex)
#else
# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, )
# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return)
#endif
} // namespace testing
#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_

View File

@ -0,0 +1,250 @@
// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
//
// The Google C++ Testing Framework (Google Test)
//
// This header file defines the Message class.
//
// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
// leave some internal implementation details in this header file.
// They are clearly marked by comments like this:
//
// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
//
// Such code is NOT meant to be used by a user directly, and is subject
// 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_
#include <limits>
#include "gtest/internal/gtest-port.h"
// 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);
namespace testing {
// The Message class works like an ostream repeater.
//
// Typical usage:
//
// 1. You stream a bunch of values to a Message object.
// It will remember the text in a stringstream.
// 2. Then you stream the Message object to an ostream.
// This causes the text in the Message to be streamed
// to the ostream.
//
// For example;
//
// testing::Message foo;
// foo << 1 << " != " << 2;
// std::cout << foo;
//
// will print "1 != 2".
//
// Message is not intended to be inherited from. In particular, its
// destructor is not virtual.
//
// Note that stringstream behaves differently in gcc and in MSVC. You
// can stream a NULL char pointer to it in the former, but not in the
// latter (it causes an access violation if you do). The Message
// class hides this difference by treating a NULL char pointer as
// "(null)".
class GTEST_API_ Message {
private:
// The type of basic IO manipulators (endl, ends, and flush) for
// narrow streams.
typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&);
public:
// Constructs an empty Message.
Message();
// Copy constructor.
Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT
*ss_ << msg.GetString();
}
// Constructs a Message from a C-string.
explicit Message(const char* str) : ss_(new ::std::stringstream) {
*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) {
// Some libraries overload << for STL containers. These
// overloads are defined in the global namespace instead of ::std.
//
// C++'s symbol lookup rule (i.e. Koenig lookup) says that these
// overloads are visible in either the std namespace or the global
// namespace, but not other namespaces, including the testing
// namespace which Google Test's Message class is in.
//
// To allow STL containers (and other types that has a << operator
// defined in the global namespace) to be used in Google Test
// assertions, testing::Message must access the custom << operator
// from the global namespace. With this using declaration,
// overloads of << defined in the global namespace and those
// visible via Koenig lookup are both exposed in this function.
using ::operator <<;
*ss_ << val;
return *this;
}
// Streams a pointer value to this object.
//
// This function is an overload of the previous one. When you
// stream a pointer to a Message, this definition will be used as it
// is more specialized. (The C++ Standard, section
// [temp.func.order].) If you stream a non-pointer, then the
// previous definition will be used.
//
// The reason for this overload is that streaming a NULL pointer to
// ostream is undefined behavior. Depending on the compiler, you
// may get "0", "(nil)", "(null)", or an access violation. To
// ensure consistent result across compilers, we always treat NULL
// as "(null)".
template <typename T>
inline Message& operator <<(T* const& pointer) { // NOLINT
if (pointer == NULL) {
*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
// of operator <<, even though its body is the same as the
// templatized version above. Without this definition, streaming
// endl or other basic IO manipulators to Message will confuse the
// compiler.
Message& operator <<(BasicNarrowIoManip val) {
*ss_ << val;
return *this;
}
// Instead of 1/0, we want to see true/false for bool values.
Message& operator <<(bool b) {
return *this << (b ? "true" : "false");
}
// These two overloads allow streaming a wide C string to a Message
// using the UTF-8 encoding.
Message& operator <<(const wchar_t* wide_c_str);
Message& operator <<(wchar_t* wide_c_str);
#if GTEST_HAS_STD_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 ::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".
//
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
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_;
// We declare (but don't implement) this to prevent the compiler
// from implementing the assignment operator.
void operator=(const Message&);
};
// Streams a Message to an ostream.
inline std::ostream& operator <<(std::ostream& os, const Message& sb) {
return os << sb.GetString();
}
namespace internal {
// Converts a streamable value to an std::string. A NULL pointer is
// converted to "(null)". When the input value is a ::string,
// ::std::string, ::wstring, or ::std::wstring object, each NUL
// character in it is replaced with "\\0".
template <typename T>
std::string StreamableToString(const T& streamable) {
return (Message() << streamable).GetString();
}
} // namespace internal
} // namespace testing
#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,855 @@
// Copyright 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
// Google Test - The Google C++ Testing Framework
//
// This file implements a universal value printer that can print a
// value of any type T:
//
// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
//
// A user can teach this function how to print a class type T by
// defining either operator<<() or PrintTo() in the namespace that
// defines T. More specifically, the FIRST defined function in the
// following list will be used (assuming T is defined in namespace
// foo):
//
// 1. foo::PrintTo(const T&, ostream*)
// 2. operator<<(ostream&, const T&) defined in either foo or the
// global namespace.
//
// If none of the above is defined, it will print the debug string of
// the value if it is a protocol buffer, or print the raw bytes in the
// value otherwise.
//
// To aid debugging: when T is a reference type, the address of the
// value is also printed; when T is a (const) char pointer, both the
// pointer value and the NUL-terminated string it points to are
// printed.
//
// We also provide some convenient wrappers:
//
// // Prints a value to a string. For a (const or not) char
// // pointer, the NUL-terminated string (but not the pointer) is
// // printed.
// std::string ::testing::PrintToString(const T& value);
//
// // Prints a value tersely: for a reference type, the referenced
// // value (but not the address) is printed; for a (const or not) char
// // pointer, the NUL-terminated string (but not the pointer) is
// // printed.
// void ::testing::internal::UniversalTersePrint(const T& value, ostream*);
//
// // Prints value using the type inferred by the compiler. The difference
// // from UniversalTersePrint() is that this function prints both the
// // pointer and the NUL-terminated string for a (const or not) char pointer.
// void ::testing::internal::UniversalPrint(const T& value, ostream*);
//
// // Prints the fields of a tuple tersely to a string vector, one
// // element for each field. Tuple support must be enabled in
// // gtest-port.h.
// std::vector<string> UniversalTersePrintTupleFieldsToStrings(
// const Tuple& value);
//
// Known limitation:
//
// The print primitives print the elements of an STL-style container
// using the compiler-inferred type of *iter where iter is a
// const_iterator of the container. When const_iterator is an input
// iterator but not a forward iterator, this inferred type may not
// match value_type, and the print output may be incorrect. In
// practice, this is rarely a problem as for most containers
// const_iterator is a forward iterator. We'll fix this if there's an
// actual need for it. Note that this fix cannot rely on value_type
// being defined as many user-defined container types don't have
// value_type.
#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
#include <ostream> // NOLINT
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "gtest/internal/gtest-port.h"
#include "gtest/internal/gtest-internal.h"
namespace testing {
// Definitions in the 'internal' and 'internal2' name spaces are
// subject to change without notice. DO NOT USE THEM IN USER CODE!
namespace internal2 {
// Prints the given number of bytes in the given object to the given
// ostream.
GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes,
size_t count,
::std::ostream* os);
// For selecting which printer to use when a given type has neither <<
// nor PrintTo().
enum TypeKind {
kProtobuf, // a protobuf type
kConvertibleToInteger, // a type implicitly convertible to BiggestInt
// (e.g. a named or unnamed enum type)
kOtherType // anything else
};
// TypeWithoutFormatter<T, kTypeKind>::PrintValue(value, os) is called
// by the universal printer to print a value of type T when neither
// operator<< nor PrintTo() is defined for T, where kTypeKind is the
// "kind" of T as defined by enum TypeKind.
template <typename T, TypeKind kTypeKind>
class TypeWithoutFormatter {
public:
// This default version is called when kTypeKind is kOtherType.
static void PrintValue(const T& value, ::std::ostream* os) {
PrintBytesInObjectTo(reinterpret_cast<const unsigned char*>(&value),
sizeof(value), os);
}
};
// We print a protobuf using its ShortDebugString() when the string
// doesn't exceed this many characters; otherwise we print it using
// DebugString() for better readability.
const size_t kProtobufOneLinerMaxLength = 50;
template <typename T>
class TypeWithoutFormatter<T, kProtobuf> {
public:
static void PrintValue(const T& value, ::std::ostream* os) {
const ::testing::internal::string short_str = value.ShortDebugString();
const ::testing::internal::string pretty_str =
short_str.length() <= kProtobufOneLinerMaxLength ?
short_str : ("\n" + value.DebugString());
*os << ("<" + pretty_str + ">");
}
};
template <typename T>
class TypeWithoutFormatter<T, kConvertibleToInteger> {
public:
// Since T has no << operator or PrintTo() but can be implicitly
// converted to BiggestInt, we print it as a BiggestInt.
//
// Most likely T is an enum type (either named or unnamed), in which
// case printing it as an integer is the desired behavior. In case
// T is not an enum, printing it as an integer is the best we can do
// given that it has no user-defined printer.
static void PrintValue(const T& value, ::std::ostream* os) {
const internal::BiggestInt kBigInt = value;
*os << kBigInt;
}
};
// Prints the given value to the given ostream. If the value is a
// protocol message, its debug string is printed; if it's an enum or
// of a type implicitly convertible to BiggestInt, it's printed as an
// integer; otherwise the bytes in the value are printed. This is
// what UniversalPrinter<T>::Print() does when it knows nothing about
// type T and T has neither << operator nor PrintTo().
//
// A user can override this behavior for a class type Foo by defining
// a << operator in the namespace where Foo is defined.
//
// We put this operator in namespace 'internal2' instead of 'internal'
// to simplify the implementation, as much code in 'internal' needs to
// use << in STL, which would conflict with our own << were it defined
// in 'internal'.
//
// Note that this operator<< takes a generic std::basic_ostream<Char,
// CharTraits> type instead of the more restricted std::ostream. If
// we define it to take an std::ostream instead, we'll get an
// "ambiguous overloads" compiler error when trying to print a type
// Foo that supports streaming to std::basic_ostream<Char,
// CharTraits>, as the compiler cannot tell whether
// operator<<(std::ostream&, const T&) or
// operator<<(std::basic_stream<Char, CharTraits>, const Foo&) is more
// specific.
template <typename Char, typename CharTraits, typename T>
::std::basic_ostream<Char, CharTraits>& operator<<(
::std::basic_ostream<Char, CharTraits>& os, const T& x) {
TypeWithoutFormatter<T,
(internal::IsAProtocolMessage<T>::value ? kProtobuf :
internal::ImplicitlyConvertible<const T&, internal::BiggestInt>::value ?
kConvertibleToInteger : kOtherType)>::PrintValue(x, &os);
return os;
}
} // namespace internal2
} // namespace testing
// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up
// magic needed for implementing UniversalPrinter won't work.
namespace testing_internal {
// Used to print a value that is not an STL-style container when the
// user doesn't define PrintTo() for it.
template <typename T>
void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) {
// With the following statement, during unqualified name lookup,
// testing::internal2::operator<< appears as if it was declared in
// the nearest enclosing namespace that contains both
// ::testing_internal and ::testing::internal2, i.e. the global
// namespace. For more details, refer to the C++ Standard section
// 7.3.4-1 [namespace.udir]. This allows us to fall back onto
// testing::internal2::operator<< in case T doesn't come with a <<
// operator.
//
// We cannot write 'using ::testing::internal2::operator<<;', which
// gcc 3.3 fails to compile due to a compiler bug.
using namespace ::testing::internal2; // NOLINT
// Assuming T is defined in namespace foo, in the next statement,
// the compiler will consider all of:
//
// 1. foo::operator<< (thanks to Koenig look-up),
// 2. ::operator<< (as the current namespace is enclosed in ::),
// 3. testing::internal2::operator<< (thanks to the using statement above).
//
// The operator<< whose type matches T best will be picked.
//
// We deliberately allow #2 to be a candidate, as sometimes it's
// impossible to define #1 (e.g. when foo is ::std, defining
// anything in it is undefined behavior unless you are a compiler
// vendor.).
*os << value;
}
} // namespace testing_internal
namespace testing {
namespace internal {
// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given
// value to the given ostream. The caller must ensure that
// 'ostream_ptr' is not NULL, or the behavior is undefined.
//
// We define UniversalPrinter as a class template (as opposed to a
// function template), as we need to partially specialize it for
// reference types, which cannot be done with function templates.
template <typename T>
class UniversalPrinter;
template <typename T>
void UniversalPrint(const T& value, ::std::ostream* os);
// Used to print an STL-style container when the user doesn't define
// a PrintTo() for it.
template <typename C>
void DefaultPrintTo(IsContainer /* dummy */,
false_type /* is not a pointer */,
const C& container, ::std::ostream* os) {
const size_t kMaxCount = 32; // The maximum number of elements to print.
*os << '{';
size_t count = 0;
for (typename C::const_iterator it = container.begin();
it != container.end(); ++it, ++count) {
if (count > 0) {
*os << ',';
if (count == kMaxCount) { // Enough has been printed.
*os << " ...";
break;
}
}
*os << ' ';
// We cannot call PrintTo(*it, os) here as PrintTo() doesn't
// handle *it being a native array.
internal::UniversalPrint(*it, os);
}
if (count > 0) {
*os << ' ';
}
*os << '}';
}
// Used to print a pointer that is neither a char pointer nor a member
// pointer, when the user doesn't define PrintTo() for it. (A member
// variable pointer or member function pointer doesn't really point to
// a location in the address space. Their representation is
// implementation-defined. Therefore they will be printed as raw
// bytes.)
template <typename T>
void DefaultPrintTo(IsNotContainer /* dummy */,
true_type /* is a pointer */,
T* p, ::std::ostream* os) {
if (p == NULL) {
*os << "NULL";
} else {
// C++ doesn't allow casting from a function pointer to any object
// pointer.
//
// IsTrue() silences warnings: "Condition is always true",
// "unreachable code".
if (IsTrue(ImplicitlyConvertible<T*, const void*>::value)) {
// T is not a function type. We just call << to print p,
// relying on ADL to pick up user-defined << for their pointer
// types, if any.
*os << p;
} else {
// T is a function type, so '*os << p' doesn't do what we want
// (it just prints p as bool). We want to print p as a const
// void*. However, we cannot cast it to const void* directly,
// even using reinterpret_cast, as earlier versions of gcc
// (e.g. 3.4.5) cannot compile the cast when p is a function
// pointer. Casting to UInt64 first solves the problem.
*os << reinterpret_cast<const void*>(
reinterpret_cast<internal::UInt64>(p));
}
}
}
// Used to print a non-container, non-pointer value when the user
// doesn't define PrintTo() for it.
template <typename T>
void DefaultPrintTo(IsNotContainer /* dummy */,
false_type /* is not a pointer */,
const T& value, ::std::ostream* os) {
::testing_internal::DefaultPrintNonContainerTo(value, os);
}
// Prints the given value using the << operator if it has one;
// otherwise prints the bytes in it. This is what
// UniversalPrinter<T>::Print() does when PrintTo() is not specialized
// or overloaded for type T.
//
// A user can override this behavior for a class type Foo by defining
// an overload of PrintTo() in the namespace where Foo is defined. We
// give the user this option as sometimes defining a << operator for
// Foo is not desirable (e.g. the coding style may prevent doing it,
// or there is already a << operator but it doesn't do what the user
// wants).
template <typename T>
void PrintTo(const T& value, ::std::ostream* os) {
// DefaultPrintTo() is overloaded. The type of its first two
// arguments determine which version will be picked. If T is an
// STL-style container, the version for container will be called; if
// T is a pointer, the pointer version will be called; otherwise the
// generic version will be called.
//
// Note that we check for container types here, prior to we check
// for protocol message types in our operator<<. The rationale is:
//
// For protocol messages, we want to give people a chance to
// override Google Mock's format by defining a PrintTo() or
// operator<<. For STL containers, other formats can be
// incompatible with Google Mock's format for the container
// elements; therefore we check for container types here to ensure
// that our format is used.
//
// The second argument of DefaultPrintTo() is needed to bypass a bug
// in Symbian's C++ compiler that prevents it from picking the right
// overload between:
//
// PrintTo(const T& x, ...);
// PrintTo(T* x, ...);
DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os);
}
// The following list of PrintTo() overloads tells
// UniversalPrinter<T>::Print() how to print standard types (built-in
// types, strings, plain arrays, and pointers).
// Overloads for various char types.
GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os);
GTEST_API_ void PrintTo(signed char c, ::std::ostream* os);
inline void PrintTo(char c, ::std::ostream* os) {
// When printing a plain char, we always treat it as unsigned. This
// way, the output won't be affected by whether the compiler thinks
// char is signed or not.
PrintTo(static_cast<unsigned char>(c), os);
}
// Overloads for other simple built-in types.
inline void PrintTo(bool x, ::std::ostream* os) {
*os << (x ? "true" : "false");
}
// Overload for wchar_t type.
// Prints a wchar_t as a symbol if it is printable or as its internal
// code otherwise and also as its decimal code (except for L'\0').
// The L'\0' char is printed as "L'\\0'". The decimal code is printed
// as signed integer when wchar_t is implemented by the compiler
// as a signed type and is printed as an unsigned integer when wchar_t
// is implemented as an unsigned type.
GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os);
// Overloads for C strings.
GTEST_API_ void PrintTo(const char* s, ::std::ostream* os);
inline void PrintTo(char* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const char*>(s), os);
}
// signed/unsigned char is often used for representing binary data, so
// we print pointers to it as void* to be safe.
inline void PrintTo(const signed char* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
}
inline void PrintTo(signed char* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
}
inline void PrintTo(const unsigned char* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
}
inline void PrintTo(unsigned char* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const void*>(s), os);
}
// MSVC can be configured to define wchar_t as a typedef of unsigned
// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native
// type. When wchar_t is a typedef, defining an overload for const
// wchar_t* would cause unsigned short* be printed as a wide string,
// possibly causing invalid memory accesses.
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
// Overloads for wide C strings
GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os);
inline void PrintTo(wchar_t* s, ::std::ostream* os) {
PrintTo(ImplicitCast_<const wchar_t*>(s), os);
}
#endif
// Overload for C arrays. Multi-dimensional arrays are printed
// properly.
// Prints the given number of elements in an array, without printing
// the curly braces.
template <typename T>
void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) {
UniversalPrint(a[0], os);
for (size_t i = 1; i != count; i++) {
*os << ", ";
UniversalPrint(a[i], os);
}
}
// Overloads for ::string and ::std::string.
#if GTEST_HAS_GLOBAL_STRING
GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os);
inline void PrintTo(const ::string& s, ::std::ostream* os) {
PrintStringTo(s, os);
}
#endif // GTEST_HAS_GLOBAL_STRING
GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os);
inline void PrintTo(const ::std::string& s, ::std::ostream* os) {
PrintStringTo(s, os);
}
// Overloads for ::wstring and ::std::wstring.
#if GTEST_HAS_GLOBAL_WSTRING
GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os);
inline void PrintTo(const ::wstring& s, ::std::ostream* os) {
PrintWideStringTo(s, os);
}
#endif // GTEST_HAS_GLOBAL_WSTRING
#if GTEST_HAS_STD_WSTRING
GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os);
inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
PrintWideStringTo(s, os);
}
#endif // GTEST_HAS_STD_WSTRING
#if GTEST_HAS_TR1_TUPLE
// Overload for ::std::tr1::tuple. Needed for printing function arguments,
// which are packed as tuples.
// Helper function for printing a tuple. T must be instantiated with
// a tuple type.
template <typename T>
void PrintTupleTo(const T& t, ::std::ostream* os);
// Overloaded PrintTo() for tuples of various arities. We support
// tuples of up-to 10 fields. The following implementation works
// regardless of whether tr1::tuple is implemented using the
// non-standard variadic template feature or not.
inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) {
PrintTupleTo(t, os);
}
template <typename T1>
void PrintTo(const ::std::tr1::tuple<T1>& t, ::std::ostream* os) {
PrintTupleTo(t, os);
}
template <typename T1, typename T2>
void PrintTo(const ::std::tr1::tuple<T1, T2>& t, ::std::ostream* os) {
PrintTupleTo(t, os);
}
template <typename T1, typename T2, typename T3>
void PrintTo(const ::std::tr1::tuple<T1, T2, T3>& t, ::std::ostream* os) {
PrintTupleTo(t, os);
}
template <typename T1, typename T2, typename T3, typename T4>
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4>& t, ::std::ostream* os) {
PrintTupleTo(t, os);
}
template <typename T1, typename T2, typename T3, typename T4, typename T5>
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5>& t,
::std::ostream* os) {
PrintTupleTo(t, os);
}
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6>
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6>& t,
::std::ostream* os) {
PrintTupleTo(t, os);
}
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7>
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7>& t,
::std::ostream* os) {
PrintTupleTo(t, os);
}
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8>
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8>& t,
::std::ostream* os) {
PrintTupleTo(t, os);
}
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9>
void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>& t,
::std::ostream* os) {
PrintTupleTo(t, os);
}
template <typename T1, typename T2, typename T3, typename T4, typename T5,
typename T6, typename T7, typename T8, typename T9, typename T10>
void PrintTo(
const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>& t,
::std::ostream* os) {
PrintTupleTo(t, os);
}
#endif // GTEST_HAS_TR1_TUPLE
// Overload for std::pair.
template <typename T1, typename T2>
void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
*os << '(';
// We cannot use UniversalPrint(value.first, os) here, as T1 may be
// a reference type. The same for printing value.second.
UniversalPrinter<T1>::Print(value.first, os);
*os << ", ";
UniversalPrinter<T2>::Print(value.second, os);
*os << ')';
}
// Implements printing a non-reference type T by letting the compiler
// pick the right overload of PrintTo() for T.
template <typename T>
class UniversalPrinter {
public:
// MSVC warns about adding const to a function type, so we want to
// disable the warning.
#ifdef _MSC_VER
# pragma warning(push) // Saves the current warning state.
# pragma warning(disable:4180) // Temporarily disables warning 4180.
#endif // _MSC_VER
// Note: we deliberately don't call this PrintTo(), as that name
// conflicts with ::testing::internal::PrintTo in the body of the
// function.
static void Print(const T& value, ::std::ostream* os) {
// By default, ::testing::internal::PrintTo() is used for printing
// the value.
//
// Thanks to Koenig look-up, if T is a class and has its own
// PrintTo() function defined in its namespace, that function will
// be visible here. Since it is more specific than the generic ones
// in ::testing::internal, it will be picked by the compiler in the
// following statement - exactly what we want.
PrintTo(value, os);
}
#ifdef _MSC_VER
# pragma warning(pop) // Restores the warning state.
#endif // _MSC_VER
};
// UniversalPrintArray(begin, len, os) prints an array of 'len'
// elements, starting at address 'begin'.
template <typename T>
void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
if (len == 0) {
*os << "{}";
} else {
*os << "{ ";
const size_t kThreshold = 18;
const size_t kChunkSize = 8;
// If the array has more than kThreshold elements, we'll have to
// omit some details by printing only the first and the last
// kChunkSize elements.
// TODO(wan@google.com): let the user control the threshold using a flag.
if (len <= kThreshold) {
PrintRawArrayTo(begin, len, os);
} else {
PrintRawArrayTo(begin, kChunkSize, os);
*os << ", ..., ";
PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os);
}
*os << " }";
}
}
// This overload prints a (const) char array compactly.
GTEST_API_ void UniversalPrintArray(
const char* begin, size_t len, ::std::ostream* os);
// This overload prints a (const) wchar_t array compactly.
GTEST_API_ void UniversalPrintArray(
const wchar_t* begin, size_t len, ::std::ostream* os);
// Implements printing an array type T[N].
template <typename T, size_t N>
class UniversalPrinter<T[N]> {
public:
// Prints the given array, omitting some elements when there are too
// many.
static void Print(const T (&a)[N], ::std::ostream* os) {
UniversalPrintArray(a, N, os);
}
};
// Implements printing a reference type T&.
template <typename T>
class UniversalPrinter<T&> {
public:
// MSVC warns about adding const to a function type, so we want to
// disable the warning.
#ifdef _MSC_VER
# pragma warning(push) // Saves the current warning state.
# pragma warning(disable:4180) // Temporarily disables warning 4180.
#endif // _MSC_VER
static void Print(const T& value, ::std::ostream* os) {
// Prints the address of the value. We use reinterpret_cast here
// as static_cast doesn't compile when T is a function type.
*os << "@" << reinterpret_cast<const void*>(&value) << " ";
// Then prints the value itself.
UniversalPrint(value, os);
}
#ifdef _MSC_VER
# pragma warning(pop) // Restores the warning state.
#endif // _MSC_VER
};
// Prints a value tersely: for a reference type, the referenced value
// (but not the address) is printed; for a (const) char pointer, the
// NUL-terminated string (but not the pointer) is printed.
template <typename T>
class UniversalTersePrinter {
public:
static void Print(const T& value, ::std::ostream* os) {
UniversalPrint(value, os);
}
};
template <typename T>
class UniversalTersePrinter<T&> {
public:
static void Print(const T& value, ::std::ostream* os) {
UniversalPrint(value, os);
}
};
template <typename T, size_t N>
class UniversalTersePrinter<T[N]> {
public:
static void Print(const T (&value)[N], ::std::ostream* os) {
UniversalPrinter<T[N]>::Print(value, os);
}
};
template <>
class UniversalTersePrinter<const char*> {
public:
static void Print(const char* str, ::std::ostream* os) {
if (str == NULL) {
*os << "NULL";
} else {
UniversalPrint(string(str), os);
}
}
};
template <>
class UniversalTersePrinter<char*> {
public:
static void Print(char* str, ::std::ostream* os) {
UniversalTersePrinter<const char*>::Print(str, os);
}
};
#if GTEST_HAS_STD_WSTRING
template <>
class UniversalTersePrinter<const wchar_t*> {
public:
static void Print(const wchar_t* str, ::std::ostream* os) {
if (str == NULL) {
*os << "NULL";
} else {
UniversalPrint(::std::wstring(str), os);
}
}
};
#endif
template <>
class UniversalTersePrinter<wchar_t*> {
public:
static void Print(wchar_t* str, ::std::ostream* os) {
UniversalTersePrinter<const wchar_t*>::Print(str, os);
}
};
template <typename T>
void UniversalTersePrint(const T& value, ::std::ostream* os) {
UniversalTersePrinter<T>::Print(value, os);
}
// Prints a value using the type inferred by the compiler. The
// difference between this and UniversalTersePrint() is that for a
// (const) char pointer, this prints both the pointer and the
// NUL-terminated string.
template <typename T>
void UniversalPrint(const T& value, ::std::ostream* os) {
// A workarond for the bug in VC++ 7.1 that prevents us from instantiating
// UniversalPrinter with T directly.
typedef T T1;
UniversalPrinter<T1>::Print(value, os);
}
#if GTEST_HAS_TR1_TUPLE
typedef ::std::vector<string> Strings;
// This helper template allows PrintTo() for tuples and
// UniversalTersePrintTupleFieldsToStrings() to be defined by
// induction on the number of tuple fields. The idea is that
// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N
// fields in tuple t, and can be defined in terms of
// TuplePrefixPrinter<N - 1>.
// The inductive case.
template <size_t N>
struct TuplePrefixPrinter {
// Prints the first N fields of a tuple.
template <typename Tuple>
static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os);
*os << ", ";
UniversalPrinter<typename ::std::tr1::tuple_element<N - 1, Tuple>::type>
::Print(::std::tr1::get<N - 1>(t), os);
}
// Tersely prints the first N fields of a tuple to a string vector,
// one element for each field.
template <typename Tuple>
static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings);
::std::stringstream ss;
UniversalTersePrint(::std::tr1::get<N - 1>(t), &ss);
strings->push_back(ss.str());
}
};
// Base cases.
template <>
struct TuplePrefixPrinter<0> {
template <typename Tuple>
static void PrintPrefixTo(const Tuple&, ::std::ostream*) {}
template <typename Tuple>
static void TersePrintPrefixToStrings(const Tuple&, Strings*) {}
};
// We have to specialize the entire TuplePrefixPrinter<> class
// template here, even though the definition of
// TersePrintPrefixToStrings() is the same as the generic version, as
// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't
// support specializing a method template of a class template.
template <>
struct TuplePrefixPrinter<1> {
template <typename Tuple>
static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>::
Print(::std::tr1::get<0>(t), os);
}
template <typename Tuple>
static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
::std::stringstream ss;
UniversalTersePrint(::std::tr1::get<0>(t), &ss);
strings->push_back(ss.str());
}
};
// Helper function for printing a tuple. T must be instantiated with
// a tuple type.
template <typename T>
void PrintTupleTo(const T& t, ::std::ostream* os) {
*os << "(";
TuplePrefixPrinter< ::std::tr1::tuple_size<T>::value>::
PrintPrefixTo(t, os);
*os << ")";
}
// Prints the fields of a tuple tersely to a string vector, one
// element for each field. See the comment before
// UniversalTersePrint() for how we define "tersely".
template <typename Tuple>
Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
Strings result;
TuplePrefixPrinter< ::std::tr1::tuple_size<Tuple>::value>::
TersePrintPrefixToStrings(value, &result);
return result;
}
#endif // GTEST_HAS_TR1_TUPLE
} // namespace internal
template <typename T>
::std::string PrintToString(const T& value) {
::std::stringstream ss;
internal::UniversalTersePrinter<T>::Print(value, &ss);
return ss.str();
}
} // namespace testing
#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_

232
extern/gtest/include/gtest/gtest-spi.h vendored Normal file
View File

@ -0,0 +1,232 @@
// Copyright 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
//
// 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_
#include "gtest/gtest.h"
namespace testing {
// This helper class can be used to mock out Google Test failure reporting
// so that we can test Google Test or code that builds on Google Test.
//
// An object of this class appends a TestPartResult object to the
// TestPartResultArray object given in the constructor whenever a Google Test
// failure is reported. It can either intercept only failures that are
// generated in the same thread that created this object or it can intercept
// all generated failures. The scope of this mock object can be controlled with
// the second argument to the two arguments constructor.
class GTEST_API_ ScopedFakeTestPartResultReporter
: public TestPartResultReporterInterface {
public:
// The two possible mocking modes of this object.
enum InterceptMode {
INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures.
INTERCEPT_ALL_THREADS // Intercepts all failures.
};
// The c'tor sets this object as the test part result reporter used
// by Google Test. The 'result' parameter specifies where to report the
// results. This reporter will only catch failures generated in the current
// thread. DEPRECATED
explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result);
// Same as above, but you can choose the interception scope of this object.
ScopedFakeTestPartResultReporter(InterceptMode intercept_mode,
TestPartResultArray* result);
// The d'tor restores the previous test part result reporter.
virtual ~ScopedFakeTestPartResultReporter();
// Appends the TestPartResult object to the TestPartResultArray
// received in the constructor.
//
// This method is from the TestPartResultReporterInterface
// interface.
virtual void ReportTestPartResult(const TestPartResult& result);
private:
void Init();
const InterceptMode intercept_mode_;
TestPartResultReporterInterface* old_reporter_;
TestPartResultArray* const result_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter);
};
namespace internal {
// A helper class for implementing EXPECT_FATAL_FAILURE() and
// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given
// TestPartResultArray contains exactly one failure that has the given
// type and contains the given substring. If that's not the case, a
// non-fatal failure will be generated.
class GTEST_API_ SingleFailureChecker {
public:
// The constructor remembers the arguments.
SingleFailureChecker(const TestPartResultArray* results,
TestPartResult::Type type,
const string& substr);
~SingleFailureChecker();
private:
const TestPartResultArray* const results_;
const TestPartResult::Type type_;
const string substr_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
};
} // namespace internal
} // namespace testing
// 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'
// being part of the failure message.
//
// There are two different versions of this macro. EXPECT_FATAL_FAILURE only
// affects and considers failures generated in the current thread and
// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
//
// The verification of the assertion is done correctly even when the statement
// throws an exception or aborts the current function.
//
// Known restrictions:
// - 'statement' cannot reference local non-static variables or
// non-static members of the current object.
// - 'statement' cannot return a value.
// - You cannot stream a failure message to this macro.
//
// Note that even though the implementations of the following two
// macros are much alike, we cannot refactor them to use a common
// helper macro, due to some peculiarity in how the preprocessor
// works. The AcceptsMacroThatExpandsToUnprotectedComma test in
// gtest_unittest.cc will fail to compile if we do that.
#define EXPECT_FATAL_FAILURE(statement, substr) \
do { \
class GTestExpectFatalFailureHelper {\
public:\
static void Execute() { statement; }\
};\
::testing::TestPartResultArray gtest_failures;\
::testing::internal::SingleFailureChecker gtest_checker(\
&gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
{\
::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
::testing::ScopedFakeTestPartResultReporter:: \
INTERCEPT_ONLY_CURRENT_THREAD, &gtest_failures);\
GTestExpectFatalFailureHelper::Execute();\
}\
} while (::testing::internal::AlwaysFalse())
#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
do { \
class GTestExpectFatalFailureHelper {\
public:\
static void Execute() { statement; }\
};\
::testing::TestPartResultArray gtest_failures;\
::testing::internal::SingleFailureChecker gtest_checker(\
&gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
{\
::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
::testing::ScopedFakeTestPartResultReporter:: \
INTERCEPT_ALL_THREADS, &gtest_failures);\
GTestExpectFatalFailureHelper::Execute();\
}\
} while (::testing::internal::AlwaysFalse())
// A macro for testing Google Test assertions or code that's expected to
// generate Google Test non-fatal failures. It asserts that the given
// statement will cause exactly one non-fatal Google Test failure with 'substr'
// being part of the failure message.
//
// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only
// affects and considers failures generated in the current thread and
// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
//
// 'statement' is allowed to reference local variables and members of
// the current object.
//
// The verification of the assertion is done correctly even when the statement
// throws an exception or aborts the current function.
//
// Known restrictions:
// - You cannot stream a failure message to this macro.
//
// Note that even though the implementations of the following two
// macros are much alike, we cannot refactor them to use a common
// helper macro, due to some peculiarity in how the preprocessor
// works. If we do that, the code won't compile when the user gives
// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that
// expands to code containing an unprotected comma. The
// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc
// catches that.
//
// For the same reason, we have to write
// if (::testing::internal::AlwaysTrue()) { statement; }
// instead of
// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
// to avoid an MSVC warning on unreachable code.
#define EXPECT_NONFATAL_FAILURE(statement, substr) \
do {\
::testing::TestPartResultArray gtest_failures;\
::testing::internal::SingleFailureChecker gtest_checker(\
&gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
(substr));\
{\
::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
::testing::ScopedFakeTestPartResultReporter:: \
INTERCEPT_ONLY_CURRENT_THREAD, &gtest_failures);\
if (::testing::internal::AlwaysTrue()) { statement; }\
}\
} while (::testing::internal::AlwaysFalse())
#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
do {\
::testing::TestPartResultArray gtest_failures;\
::testing::internal::SingleFailureChecker gtest_checker(\
&gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
(substr));\
{\
::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \
&gtest_failures);\
if (::testing::internal::AlwaysTrue()) { statement; }\
}\
} while (::testing::internal::AlwaysFalse())
#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_

View File

@ -0,0 +1,179 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: mheule@google.com (Markus Heule)
//
#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
#include <iosfwd>
#include <vector>
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-string.h"
namespace testing {
// A copyable object representing the result of a test part (i.e. an
// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()).
//
// Don't inherit from TestPartResult as its destructor is not virtual.
class GTEST_API_ TestPartResult {
public:
// The possible outcomes of a test part (i.e. an assertion or an
// explicit SUCCEED(), FAIL(), or ADD_FAILURE()).
enum Type {
kSuccess, // Succeeded.
kNonFatalFailure, // Failed but the test can continue.
kFatalFailure // Failed and the test should be terminated.
};
// 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,
const char* a_message)
: type_(a_type),
file_name_(a_file_name == NULL ? "" : a_file_name),
line_number_(a_line_number),
summary_(ExtractSummary(a_message)),
message_(a_message) {
}
// Gets the outcome of the test part.
Type type() const { return type_; }
// 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();
}
// Gets the line in the source file where the test part took place,
// or -1 if it's unknown.
int line_number() const { return line_number_; }
// Gets the summary of the failure message.
const char* summary() const { return summary_.c_str(); }
// 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 iff the test part failed.
bool failed() const { return type_ != kSuccess; }
// Returns true iff the test part non-fatally failed.
bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
// Returns true iff the test part fatally failed.
bool fatally_failed() const { return type_ == kFatalFailure; }
private:
Type type_;
// Gets the summary of the failure message by omitting the stack
// trace in it.
static std::string ExtractSummary(const char* message);
// The name of the source file where the test part took place, or
// "" if the source file is unknown.
std::string file_name_;
// The line in the source file where the test part took place, or -1
// if the line number is unknown.
int line_number_;
std::string summary_; // The test failure summary.
std::string message_; // The test failure message.
};
// Prints a TestPartResult object.
std::ostream& operator<<(std::ostream& os, const TestPartResult& result);
// An array of TestPartResult objects.
//
// Don't inherit from TestPartResultArray as its destructor is not
// virtual.
class GTEST_API_ TestPartResultArray {
public:
TestPartResultArray() {}
// Appends the given TestPartResult to the array.
void Append(const TestPartResult& result);
// Returns the TestPartResult at the given index (0-based).
const TestPartResult& GetTestPartResult(int index) const;
// Returns the number of TestPartResult objects in the array.
int size() const;
private:
std::vector<TestPartResult> array_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray);
};
// This interface knows how to report a test part result.
class TestPartResultReporterInterface {
public:
virtual ~TestPartResultReporterInterface() {}
virtual void ReportTestPartResult(const TestPartResult& result) = 0;
};
namespace internal {
// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a
// statement generates new fatal failures. To do so it registers itself as the
// current test part result reporter. Besides checking if fatal failures were
// reported, it only delegates the reporting to the former result reporter.
// The original result reporter is restored in the destructor.
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
class GTEST_API_ HasNewFatalFailureHelper
: public TestPartResultReporterInterface {
public:
HasNewFatalFailureHelper();
virtual ~HasNewFatalFailureHelper();
virtual void ReportTestPartResult(const TestPartResult& result);
bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
private:
bool has_new_fatal_failure_;
TestPartResultReporterInterface* original_reporter_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper);
};
} // namespace internal
} // namespace testing
#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_

View File

@ -0,0 +1,259 @@
// Copyright 2008 Google Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
// This header implements typed tests and type-parameterized tests.
// Typed (aka type-driven) tests repeat the same test for types in a
// list. You must know which types you want to test with when writing
// typed tests. Here's how you do it:
#if 0
// First, define a fixture class template. It should be parameterized
// by a type. Remember to derive it from testing::Test.
template <typename T>
class FooTest : public testing::Test {
public:
...
typedef std::list<T> List;
static T shared_;
T value_;
};
// Next, associate a list of types with the test case, 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);
// If the type list contains only one type, you can write that type
// directly without Types<...>:
// TYPED_TEST_CASE(FooTest, int);
// Then, use TYPED_TEST() instead of TEST_F() to define as many typed
// tests for this test case 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'.
TypeParam n = this->value_;
// To visit static members of the fixture, add the TestFixture::
// prefix.
n += TestFixture::shared_;
// To refer to typedefs in the fixture, add the "typename
// TestFixture::" prefix.
typename TestFixture::List values;
values.push_back(n);
...
}
TYPED_TEST(FooTest, HasPropertyA) { ... }
#endif // 0
// Type-parameterized tests are abstract test patterns parameterized
// by a type. Compared with typed tests, type-parameterized tests
// allow you to define the test pattern without knowing what the type
// parameters are. The defined pattern can be instantiated with
// different types any number of times, in any number of translation
// units.
//
// If you are designing an interface or concept, you can define a
// suite of type-parameterized tests to verify properties that any
// valid implementation of the interface/concept should have. Then,
// each implementation can easily instantiate the test suite to verify
// that it conforms to the requirements, without having to write
// similar tests repeatedly. Here's an example:
#if 0
// First, define a fixture class template. It should be parameterized
// by a type. Remember to derive it from testing::Test.
template <typename T>
class FooTest : public testing::Test {
...
};
// Next, declare that you will define a type-parameterized test case
// (the _P suffix is for "parameterized" or "pattern", whichever you
// prefer):
TYPED_TEST_CASE_P(FooTest);
// Then, use TYPED_TEST_P() to define as many type-parameterized tests
// for this type-parameterized test case as you want.
TYPED_TEST_P(FooTest, DoesBlah) {
// Inside a test, refer to TypeParam to get the type parameter.
TypeParam n = 0;
...
}
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
// case.
REGISTER_TYPED_TEST_CASE_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
// it in multiple C++ source files and instantiate it multiple times.
//
// 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
// different instances.
typedef testing::Types<char, int, unsigned int> MyTypes;
INSTANTIATE_TYPED_TEST_CASE_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);
#endif // 0
#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(\
"", #CaseName, #TestName, 0); \
template <typename gtest_TypeParam_> \
void GTEST_TEST_CLASS_NAME_(CaseName, TestName)<gtest_TypeParam_>::TestBody()
#endif // GTEST_HAS_TYPED_TEST
// 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
// name of the namespace is subject to change without notice.
# define GTEST_CASE_NAMESPACE_(TestCaseName) \
gtest_case_##TestCaseName##_
// 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##_
// 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 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, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName))
#endif // GTEST_HAS_TYPED_TEST_P
#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_

2291
extern/gtest/include/gtest/gtest.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,358 @@
// Copyright 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command
// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND!
//
// Implements a family of generic predicate assertion macros.
#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
#define GTEST_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_
// This header implements a family of generic predicate assertion
// macros:
//
// ASSERT_PRED_FORMAT1(pred_format, v1)
// ASSERT_PRED_FORMAT2(pred_format, v1, v2)
// ...
//
// where pred_format is a function or functor that takes n (in the
// case of ASSERT_PRED_FORMATn) values and their source expression
// text, and returns a testing::AssertionResult. See the definition
// of ASSERT_EQ in gtest.h for an example.
//
// If you don't care about formatting, you can use the more
// restrictive version:
//
// ASSERT_PRED1(pred, v1)
// ASSERT_PRED2(pred, v1, v2)
// ...
//
// where pred is an n-ary function or functor that returns bool,
// and the values v1, v2, ..., must support the << operator for
// streaming to std::ostream.
//
// We also define the EXPECT_* variations.
//
// For now we only support predicates whose arity is at most 5.
// Please email googletestframework@googlegroups.com if you need
// support for higher arities.
// GTEST_ASSERT_ is the basic statement to which all of the assertions
// in this file reduce. Don't use this in your code.
#define GTEST_ASSERT_(expression, on_failure) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (const ::testing::AssertionResult gtest_ar = (expression)) \
; \
else \
on_failure(gtest_ar.failure_message())
// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use
// this in your code.
template <typename Pred,
typename T1>
AssertionResult AssertPred1Helper(const char* pred_text,
const char* e1,
Pred pred,
const T1& v1) {
if (pred(v1)) return AssertionSuccess();
return AssertionFailure() << pred_text << "("
<< e1 << ") evaluates to false, where"
<< "\n" << e1 << " evaluates to " << v1;
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1.
// Don't use this in your code.
#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\
GTEST_ASSERT_(pred_format(#v1, v1), \
on_failure)
// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use
// this in your code.
#define GTEST_PRED1_(pred, v1, on_failure)\
GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \
#v1, \
pred, \
v1), on_failure)
// Unary predicate assertion macros.
#define EXPECT_PRED_FORMAT1(pred_format, v1) \
GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_)
#define EXPECT_PRED1(pred, v1) \
GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_)
#define ASSERT_PRED_FORMAT1(pred_format, v1) \
GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_)
#define ASSERT_PRED1(pred, v1) \
GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_)
// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use
// this in your code.
template <typename Pred,
typename T1,
typename T2>
AssertionResult AssertPred2Helper(const char* pred_text,
const char* e1,
const char* e2,
Pred pred,
const T1& v1,
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;
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
// Don't use this in your code.
#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\
GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \
on_failure)
// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use
// this in your code.
#define GTEST_PRED2_(pred, v1, v2, on_failure)\
GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \
#v1, \
#v2, \
pred, \
v1, \
v2), on_failure)
// Binary predicate assertion macros.
#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
#define EXPECT_PRED2(pred, v1, v2) \
GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_)
#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_)
#define ASSERT_PRED2(pred, v1, v2) \
GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_)
// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use
// this in your code.
template <typename Pred,
typename T1,
typename T2,
typename T3>
AssertionResult AssertPred3Helper(const char* pred_text,
const char* e1,
const char* e2,
const char* e3,
Pred pred,
const T1& v1,
const T2& v2,
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;
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3.
// Don't use this in your code.
#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \
on_failure)
// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use
// this in your code.
#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\
GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \
#v1, \
#v2, \
#v3, \
pred, \
v1, \
v2, \
v3), on_failure)
// Ternary predicate assertion macros.
#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \
GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
#define EXPECT_PRED3(pred, v1, v2, v3) \
GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \
GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_)
#define ASSERT_PRED3(pred, v1, v2, v3) \
GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_)
// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use
// this in your code.
template <typename Pred,
typename T1,
typename T2,
typename T3,
typename T4>
AssertionResult AssertPred4Helper(const char* pred_text,
const char* e1,
const char* e2,
const char* e3,
const char* e4,
Pred pred,
const T1& v1,
const T2& v2,
const T3& v3,
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;
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4.
// Don't use this in your code.
#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \
on_failure)
// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use
// this in your code.
#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\
GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \
#v1, \
#v2, \
#v3, \
#v4, \
pred, \
v1, \
v2, \
v3, \
v4), on_failure)
// 4-ary predicate assertion macros.
#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
#define EXPECT_PRED4(pred, v1, v2, v3, v4) \
GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
#define ASSERT_PRED4(pred, v1, v2, v3, v4) \
GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use
// this in your code.
template <typename Pred,
typename T1,
typename T2,
typename T3,
typename T4,
typename T5>
AssertionResult AssertPred5Helper(const char* pred_text,
const char* e1,
const char* e2,
const char* e3,
const char* e4,
const char* e5,
Pred pred,
const T1& v1,
const T2& v2,
const T3& v3,
const T4& v4,
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;
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5.
// Don't use this in your code.
#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \
on_failure)
// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use
// this in your code.
#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\
GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \
#v1, \
#v2, \
#v3, \
#v4, \
#v5, \
pred, \
v1, \
v2, \
v3, \
v4, \
v5), on_failure)
// 5-ary predicate assertion macros.
#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \
GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \
GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_

58
extern/gtest/include/gtest/gtest_prod.h vendored Normal file
View File

@ -0,0 +1,58 @@
// Copyright 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
//
// Google C++ Testing Framework definitions useful in production code.
#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_
#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_
// 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
// class. For example:
//
// class MyClass {
// private:
// void MyMethod();
// FRIEND_TEST(MyClassTest, MyMethod);
// };
//
// class MyClassTest : public testing::Test {
// // ...
// };
//
// TEST_F(MyClassTest, MyMethod) {
// // Can call MyClass::MyMethod() here.
// }
#define FRIEND_TEST(test_case_name, test_name)\
friend class test_case_name##_##test_name##_Test
#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_

View File

@ -0,0 +1,319 @@
// Copyright 2005, 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.
//
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
//
// The Google C++ Testing Framework (Google Test)
//
// This header file defines internal utilities needed for implementing
// death tests. They are subject to change without notice.
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
#include "gtest/internal/gtest-internal.h"
#include <stdio.h>
namespace testing {
namespace internal {
GTEST_DECLARE_string_(internal_run_death_test);
// Names of the flags (needed for parsing Google Test flags).
const char kDeathTestStyleFlag[] = "death_test_style";
const char kDeathTestUseFork[] = "death_test_use_fork";
const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
#if GTEST_HAS_DEATH_TEST
// 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
// style, as defined by the --gtest_death_test_style and/or
// --gtest_internal_run_death_test flags.
// In describing the results of death tests, these terms are used with
// the corresponding definitions:
//
// exit status: The integer exit information in the format specified
// by wait(2)
// exit code: The integer code passed to exit(3), _exit(2), or
// returned from main()
class GTEST_API_ DeathTest {
public:
// Create returns false if there was an error determining the
// appropriate action to take for the current death test; for example,
// if the gtest_death_test_style flag is set to an invalid value.
// The LastMessage method will return a more detailed message in that
// case. Otherwise, the DeathTest pointer pointed to by the "test"
// 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,
const char* file, int line, DeathTest** test);
DeathTest();
virtual ~DeathTest() { }
// A helper class that aborts a death test when it's deleted.
class ReturnSentinel {
public:
explicit ReturnSentinel(DeathTest* test) : test_(test) { }
~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); }
private:
DeathTest* const test_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel);
} GTEST_ATTRIBUTE_UNUSED_;
// An enumeration of possible roles that may be taken when a death
// test is encountered. EXECUTE means that the death test logic should
// be executed immediately. OVERSEE means that the program should prepare
// the appropriate environment for a child process to execute the death
// test, then wait for it to complete.
enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
// An enumeration of the three reasons that a test might be aborted.
enum AbortReason {
TEST_ENCOUNTERED_RETURN_STATEMENT,
TEST_THREW_EXCEPTION,
TEST_DID_NOT_DIE
};
// Assumes one of the above roles.
virtual TestRole AssumeRole() = 0;
// Waits for the death test to finish and returns its status.
virtual int Wait() = 0;
// Returns true if the death test passed; that is, the test process
// exited during the test, its exit status matches a user-supplied
// predicate, and its stderr output matches a user-supplied regular
// expression.
// The user-supplied predicate may be a macro expression rather
// than a function pointer or functor, or else Wait and Passed could
// be combined.
virtual bool Passed(bool exit_status_ok) = 0;
// Signals that the death test did not die as expected.
virtual void Abort(AbortReason reason) = 0;
// Returns a human-readable outcome message regarding the outcome of
// the last death test.
static const char* LastMessage();
static void set_last_death_test_message(const std::string& message);
private:
// A string containing a description of the outcome of the last death test.
static std::string last_death_test_message_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest);
};
// 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;
};
// 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);
};
// 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);
// Traps C++ exceptions escaping statement and reports them as test
// failures. Note that trapping SEH exceptions is not implemented here.
# if GTEST_HAS_EXCEPTIONS
# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
try { \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
} catch (const ::std::exception& gtest_exception) { \
fprintf(\
stderr, \
"\n%s: Caught std::exception-derived exception escaping the " \
"death test statement. Exception message: %s\n", \
::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \
gtest_exception.what()); \
fflush(stderr); \
death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
} catch (...) { \
death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
}
# else
# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
# endif
// 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())
// 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 \
::testing::Message()
// A class representing the parsed contents of the
// --gtest_internal_run_death_test flag, as it existed when
// RUN_ALL_TESTS was called.
class InternalRunDeathTestFlag {
public:
InternalRunDeathTestFlag(const std::string& a_file,
int a_line,
int an_index,
int a_write_fd)
: file_(a_file), line_(a_line), index_(an_index),
write_fd_(a_write_fd) {}
~InternalRunDeathTestFlag() {
if (write_fd_ >= 0)
posix::Close(write_fd_);
}
const std::string& file() const { return file_; }
int line() const { return line_; }
int index() const { return index_; }
int write_fd() const { return write_fd_; }
private:
std::string file_;
int line_;
int index_;
int write_fd_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag);
};
// Returns a newly created InternalRunDeathTestFlag object with fields
// initialized from the GTEST_FLAG(internal_run_death_test) flag if
// 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_

View File

@ -0,0 +1,206 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: keith.ray@gmail.com (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>.
// Do not include this header file separately!
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
#include "gtest/internal/gtest-string.h"
namespace testing {
namespace internal {
// FilePath - a class for file and directory pathname manipulation which
// handles platform-specific conventions (like the pathname separator).
// Used for helper functions for naming files in a directory for xml output.
// Except for Set methods, all methods are const or static, which provides an
// "immutable value object" -- useful for peace of mind.
// A FilePath with a value ending in a path separator ("like/this/") represents
// a directory, otherwise it is assumed to represent a file. In either case,
// it may or may not represent an actual file or directory in the file system.
// Names are NOT checked for syntax correctness -- no checking for illegal
// characters, malformed paths, etc.
class GTEST_API_ FilePath {
public:
FilePath() : pathname_("") { }
FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { }
explicit FilePath(const std::string& pathname) : pathname_(pathname) {
Normalize();
}
FilePath& operator=(const FilePath& rhs) {
Set(rhs);
return *this;
}
void Set(const FilePath& rhs) {
pathname_ = rhs.pathname_;
}
const std::string& string() const { return pathname_; }
const char* c_str() const { return pathname_.c_str(); }
// Returns the current working directory, or "" if unsuccessful.
static FilePath GetCurrentDir();
// Given directory = "dir", base_name = "test", number = 0,
// extension = "xml", returns "dir/test.xml". If number is greater
// than zero (e.g., 12), returns "dir/test_12.xml".
// On Windows platform, uses \ as the separator rather than /.
static FilePath MakeFileName(const FilePath& directory,
const FilePath& base_name,
int number,
const char* extension);
// Given directory = "dir", relative_path = "test.xml",
// returns "dir/test.xml".
// On Windows, uses \ as the separator rather than /.
static FilePath ConcatPaths(const FilePath& directory,
const FilePath& relative_path);
// Returns a pathname for a file that does not currently exist. The pathname
// will be directory/base_name.extension or
// directory/base_name_<number>.extension if directory/base_name.extension
// already exists. The number will be incremented until a pathname is found
// that does not already exist.
// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
// There could be a race condition if two or more processes are calling this
// function at the same time -- they could both pick the same filename.
static FilePath GenerateUniqueFileName(const FilePath& directory,
const FilePath& base_name,
const char* extension);
// Returns true iff the path is "".
bool IsEmpty() const { return pathname_.empty(); }
// If input name has a trailing separator character, removes it and returns
// the name, otherwise return the name string unmodified.
// On Windows platform, uses \ as the separator, other platforms use /.
FilePath RemoveTrailingPathSeparator() const;
// Returns a copy of the FilePath with the directory part removed.
// Example: FilePath("path/to/file").RemoveDirectoryName() returns
// FilePath("file"). If there is no directory part ("just_a_file"), it returns
// the FilePath unmodified. If there is no file part ("just_a_dir/") it
// returns an empty FilePath ("").
// On Windows platform, '\' is the path separator, otherwise it is '/'.
FilePath RemoveDirectoryName() const;
// RemoveFileName returns the directory path with the filename removed.
// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
// On Windows platform, '\' is the path separator, otherwise it is '/'.
FilePath RemoveFileName() const;
// Returns a copy of the FilePath with the case-insensitive extension removed.
// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
// FilePath("dir/file"). If a case-insensitive extension is not
// found, returns a copy of the original FilePath.
FilePath RemoveExtension(const char* extension) const;
// Creates directories so that path exists. Returns true if successful or if
// the directories already exist; returns false if unable to create
// directories for any reason. Will also return false if the FilePath does
// not represent a directory (that is, it doesn't end with a path separator).
bool CreateDirectoriesRecursively() const;
// Create the directory so that path exists. Returns true if successful or
// if the directory already exists; returns false if unable to create the
// directory for any reason, including if the parent directory does not
// exist. Not named "CreateDirectory" because that's a macro on Windows.
bool CreateFolder() const;
// Returns true if FilePath describes something in the file-system,
// either a file, directory, or whatever, and that something exists.
bool FileOrDirectoryExists() const;
// Returns true if pathname describes a directory in the file-system
// that exists.
bool DirectoryExists() const;
// Returns true if FilePath ends with a path separator, which indicates that
// it is intended to represent a directory. Returns false otherwise.
// This does NOT check that a directory (or file) actually exists.
bool IsDirectory() const;
// Returns true if pathname describes a root directory. (Windows has one
// root directory per disk drive.)
bool IsRootDirectory() const;
// Returns true if pathname describes an absolute path.
bool IsAbsolutePath() const;
private:
// Replaces multiple consecutive separators with a single separator.
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
// redundancies that might be in a pathname involving "." or "..".
//
// A pathname with multiple consecutive separators may occur either through
// user error or as a result of some scripts or APIs that generate a pathname
// with a trailing separator. On other platforms the same API or script
// may NOT generate a pathname with a trailing "/". Then elsewhere that
// pathname may have another "/" and pathname components added to it,
// without checking for the separator already being there.
// The script language and operating system may allow paths like "foo//bar"
// but some of the functions in FilePath will not handle that correctly. In
// particular, RemoveTrailingPathSeparator() only removes one separator, and
// it is called in CreateDirectoriesRecursively() assuming that it will change
// a pathname from directory syntax (trailing separator) to filename syntax.
//
// On Windows this method also replaces the alternate path separator '/' with
// the primary path separator '\\', so that for example "bar\\/\\foo" becomes
// "bar\\foo".
void Normalize();
// Returns a pointer to the last occurence 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;
std::string pathname_;
}; // class FilePath
} // namespace internal
} // namespace testing
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,233 @@
// Copyright 2003 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.
//
// Authors: Dan Egnor (egnor@google.com)
//
// A "smart" pointer type with reference tracking. Every pointer to a
// particular object is kept on a circular linked list. When the last pointer
// to an object is destroyed or reassigned, the object is deleted.
//
// Used properly, this deletes the object when the last reference goes away.
// There are several caveats:
// - Like all reference counting schemes, cycles lead to leaks.
// - Each smart pointer is actually two pointers (8 bytes instead of 4).
// - Every time a pointer is assigned, the entire list of pointers to that
// object is traversed. This class is therefore NOT SUITABLE when there
// will often be more than two or three pointers to a particular object.
// - References are only tracked as long as linked_ptr<> objects are copied.
// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
// will happen (double deletion).
//
// A good use of this class is storing object references in STL containers.
// You can safely put linked_ptr<> in a vector<>.
// Other uses may not be as good.
//
// Note: If you use an incomplete type with linked_ptr<>, the class
// *containing* linked_ptr<> must have a constructor and destructor (even
// if they do nothing!).
//
// Bill Gibbons suggested we use something like this.
//
// Thread Safety:
// Unlike other linked_ptr implementations, in this implementation
// a linked_ptr object is thread-safe in the sense that:
// - it's safe to copy linked_ptr objects concurrently,
// - it's safe to copy *from* a linked_ptr and read its underlying
// raw pointer (e.g. via get()) concurrently, and
// - it's safe to write to two linked_ptrs that point to the same
// shared object concurrently.
// TODO(wan@google.com): rename this to safe_linked_ptr to avoid
// confusion with normal linked_ptr.
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_
#include <stdlib.h>
#include <assert.h>
#include "gtest/internal/gtest-port.h"
namespace testing {
namespace internal {
// Protects copying of all linked_ptr objects.
GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex);
// This is used internally by all instances of linked_ptr<>. It needs to be
// a non-template class because different types of linked_ptr<> can refer to
// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
// So, it needs to be possible for different types of linked_ptr to participate
// in the same circular linked list, so we need a single class type here.
//
// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
class linked_ptr_internal {
public:
// Create a new circle that includes only this instance.
void join_new() {
next_ = this;
}
// Many linked_ptr operations may change p.link_ for some linked_ptr
// variable p in the same circle as this object. Therefore we need
// to prevent two such operations from occurring concurrently.
//
// Note that different types of linked_ptr objects can coexist in a
// circle (e.g. linked_ptr<Base>, linked_ptr<Derived1>, and
// linked_ptr<Derived2>). Therefore we must use a single mutex to
// protect all linked_ptr objects. This can create serious
// contention in production code, but is acceptable in a testing
// framework.
// Join an existing circle.
void join(linked_ptr_internal const* ptr)
GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
MutexLock lock(&g_linked_ptr_mutex);
linked_ptr_internal const* p = ptr;
while (p->next_ != ptr) p = p->next_;
p->next_ = this;
next_ = ptr;
}
// Leave whatever circle we're part of. Returns true if we were the
// last member of the circle. Once this is done, you can join() another.
bool depart()
GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) {
MutexLock lock(&g_linked_ptr_mutex);
if (next_ == this) return true;
linked_ptr_internal const* p = next_;
while (p->next_ != this) p = p->next_;
p->next_ = next_;
return false;
}
private:
mutable linked_ptr_internal const* next_;
};
template <typename T>
class linked_ptr {
public:
typedef T element_type;
// Take over ownership of a raw pointer. This should happen as soon as
// possible after the object is created.
explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
~linked_ptr() { depart(); }
// Copy an existing linked_ptr<>, adding ourselves to the list of references.
template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
linked_ptr(linked_ptr const& ptr) { // NOLINT
assert(&ptr != this);
copy(&ptr);
}
// Assignment releases the old value and acquires the new.
template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
depart();
copy(&ptr);
return *this;
}
linked_ptr& operator=(linked_ptr const& ptr) {
if (&ptr != this) {
depart();
copy(&ptr);
}
return *this;
}
// Smart pointer members.
void reset(T* ptr = NULL) {
depart();
capture(ptr);
}
T* get() const { return value_; }
T* operator->() const { return value_; }
T& operator*() const { return *value_; }
bool operator==(T* p) const { return value_ == p; }
bool operator!=(T* p) const { return value_ != p; }
template <typename U>
bool operator==(linked_ptr<U> const& ptr) const {
return value_ == ptr.get();
}
template <typename U>
bool operator!=(linked_ptr<U> const& ptr) const {
return value_ != ptr.get();
}
private:
template <typename U>
friend class linked_ptr;
T* value_;
linked_ptr_internal link_;
void depart() {
if (link_.depart()) delete value_;
}
void capture(T* ptr) {
value_ = ptr;
link_.join_new();
}
template <typename U> void copy(linked_ptr<U> const* ptr) {
value_ = ptr->get();
if (value_)
link_.join(&ptr->link_);
else
link_.join_new();
}
};
template<typename T> inline
bool operator==(T* ptr, const linked_ptr<T>& x) {
return ptr == x.get();
}
template<typename T> inline
bool operator!=(T* ptr, const linked_ptr<T>& x) {
return ptr != x.get();
}
// A function to convert T* into linked_ptr<T>
// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
template <typename T>
linked_ptr<T> make_linked_ptr(T* ptr) {
return linked_ptr<T>(ptr);
}
} // namespace internal
} // namespace testing
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,619 @@
// Copyright 2008 Google Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: vladl@google.com (Vlad Losev)
// 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_
#include <iterator>
#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
namespace testing {
namespace internal {
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// Outputs a message explaining invalid registration of different
// fixture class for the same test case. 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,
const char* file, int line);
template <typename> class ParamGeneratorInterface;
template <typename> class ParamGenerator;
// Interface for iterating over elements provided by an implementation
// of ParamGeneratorInterface<T>.
template <typename T>
class ParamIteratorInterface {
public:
virtual ~ParamIteratorInterface() {}
// A pointer to the base generator instance.
// Used only for the purposes of iterator comparison
// to make sure that two iterators belong to the same generator.
virtual const ParamGeneratorInterface<T>* BaseGenerator() const = 0;
// Advances iterator to point to the next element
// provided by the generator. The caller is responsible
// for not calling Advance() on an iterator equal to
// BaseGenerator()->End().
virtual void Advance() = 0;
// Clones the iterator object. Used for implementing copy semantics
// of ParamIterator<T>.
virtual ParamIteratorInterface* Clone() const = 0;
// Dereferences the current iterator and provides (read-only) access
// to the pointed value. It is the caller's responsibility not to call
// Current() on an iterator equal to BaseGenerator()->End().
// Used for implementing ParamGenerator<T>::operator*().
virtual const T* Current() const = 0;
// Determines whether the given iterator and other point to the same
// element in the sequence generated by the generator.
// Used for implementing ParamGenerator<T>::operator==().
virtual bool Equals(const ParamIteratorInterface& other) const = 0;
};
// Class iterating over elements provided by an implementation of
// ParamGeneratorInterface<T>. It wraps ParamIteratorInterface<T>
// and implements the const forward iterator concept.
template <typename T>
class ParamIterator {
public:
typedef T value_type;
typedef const T& reference;
typedef ptrdiff_t difference_type;
// ParamIterator assumes ownership of the impl_ pointer.
ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {}
ParamIterator& operator=(const ParamIterator& other) {
if (this != &other)
impl_.reset(other.impl_->Clone());
return *this;
}
const T& operator*() const { return *impl_->Current(); }
const T* operator->() const { return impl_->Current(); }
// Prefix version of operator++.
ParamIterator& operator++() {
impl_->Advance();
return *this;
}
// Postfix version of operator++.
ParamIterator operator++(int /*unused*/) {
ParamIteratorInterface<T>* clone = impl_->Clone();
impl_->Advance();
return ParamIterator(clone);
}
bool operator==(const ParamIterator& other) const {
return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_);
}
bool operator!=(const ParamIterator& other) const {
return !(*this == other);
}
private:
friend class ParamGenerator<T>;
explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {}
scoped_ptr<ParamIteratorInterface<T> > impl_;
};
// ParamGeneratorInterface<T> is the binary interface to access generators
// defined in other translation units.
template <typename T>
class ParamGeneratorInterface {
public:
typedef T ParamType;
virtual ~ParamGeneratorInterface() {}
// Generator interface definition
virtual ParamIteratorInterface<T>* Begin() const = 0;
virtual ParamIteratorInterface<T>* End() const = 0;
};
// Wraps ParamGeneratorInterface<T> and provides general generator syntax
// compatible with the STL Container concept.
// This class implements copy initialization semantics and the contained
// ParamGeneratorInterface<T> instance is shared among all copies
// of the original object. This is possible because that instance is immutable.
template<typename T>
class ParamGenerator {
public:
typedef ParamIterator<T> iterator;
explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {}
ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {}
ParamGenerator& operator=(const ParamGenerator& other) {
impl_ = other.impl_;
return *this;
}
iterator begin() const { return iterator(impl_->Begin()); }
iterator end() const { return iterator(impl_->End()); }
private:
linked_ptr<const ParamGeneratorInterface<T> > impl_;
};
// Generates values from a range of two comparable values. Can be used to
// generate sequences of user-defined types that implement operator+() and
// operator<().
// This class is used in the Range() function.
template <typename T, typename IncrementT>
class RangeGenerator : public ParamGeneratorInterface<T> {
public:
RangeGenerator(T begin, T end, IncrementT step)
: begin_(begin), end_(end),
step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
virtual ~RangeGenerator() {}
virtual ParamIteratorInterface<T>* Begin() const {
return new Iterator(this, begin_, 0, step_);
}
virtual ParamIteratorInterface<T>* End() const {
return new Iterator(this, end_, end_index_, step_);
}
private:
class Iterator : public ParamIteratorInterface<T> {
public:
Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
IncrementT step)
: base_(base), value_(value), index_(index), step_(step) {}
virtual ~Iterator() {}
virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
return base_;
}
virtual void Advance() {
value_ = value_ + step_;
index_++;
}
virtual ParamIteratorInterface<T>* Clone() const {
return new Iterator(*this);
}
virtual const T* Current() const { return &value_; }
virtual bool Equals(const ParamIteratorInterface<T>& other) const {
// 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 int other_index =
CheckedDowncastToActualType<const Iterator>(&other)->index_;
return index_ == other_index;
}
private:
Iterator(const Iterator& other)
: ParamIteratorInterface<T>(),
base_(other.base_), value_(other.value_), index_(other.index_),
step_(other.step_) {}
// No implementation - assignment is unsupported.
void operator=(const Iterator& other);
const ParamGeneratorInterface<T>* const base_;
T value_;
int index_;
const IncrementT step_;
}; // class RangeGenerator::Iterator
static int CalculateEndIndex(const T& begin,
const T& end,
const IncrementT& step) {
int end_index = 0;
for (T i = begin; i < end; i = i + step)
end_index++;
return end_index;
}
// No implementation - assignment is unsupported.
void operator=(const RangeGenerator& other);
const T begin_;
const T end_;
const IncrementT step_;
// The index for the end() iterator. All the elements in the generated
// sequence are indexed (0-based) to aid iterator comparison.
const int end_index_;
}; // class RangeGenerator
// Generates values from a pair of STL-style iterators. Used in the
// ValuesIn() function. The elements are copied from the source range
// since the source can be located on the stack, and the generator
// is likely to persist beyond that stack frame.
template <typename T>
class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
public:
template <typename ForwardIterator>
ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
: container_(begin, end) {}
virtual ~ValuesInIteratorRangeGenerator() {}
virtual ParamIteratorInterface<T>* Begin() const {
return new Iterator(this, container_.begin());
}
virtual ParamIteratorInterface<T>* End() const {
return new Iterator(this, container_.end());
}
private:
typedef typename ::std::vector<T> ContainerType;
class Iterator : public ParamIteratorInterface<T> {
public:
Iterator(const ParamGeneratorInterface<T>* base,
typename ContainerType::const_iterator iterator)
: base_(base), iterator_(iterator) {}
virtual ~Iterator() {}
virtual const ParamGeneratorInterface<T>* BaseGenerator() const {
return base_;
}
virtual void Advance() {
++iterator_;
value_.reset();
}
virtual ParamIteratorInterface<T>* Clone() const {
return new Iterator(*this);
}
// We need to use cached value referenced by iterator_ because *iterator_
// can return a temporary object (and of type other then T), so just
// having "return &*iterator_;" doesn't work.
// value_ is updated here and not in Advance() because Advance()
// 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_));
return value_.get();
}
virtual bool Equals(const ParamIteratorInterface<T>& other) const {
// 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;
return iterator_ ==
CheckedDowncastToActualType<const Iterator>(&other)->iterator_;
}
private:
Iterator(const Iterator& other)
// The explicit constructor call suppresses a false warning
// emitted by gcc when supplied with the -Wextra option.
: ParamIteratorInterface<T>(),
base_(other.base_),
iterator_(other.iterator_) {}
const ParamGeneratorInterface<T>* const base_;
typename ContainerType::const_iterator iterator_;
// 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,
// which is bound by the lifespan of the iterator itself.
mutable scoped_ptr<const T> value_;
}; // class ValuesInIteratorRangeGenerator::Iterator
// No implementation - assignment is unsupported.
void operator=(const ValuesInIteratorRangeGenerator& other);
const ContainerType container_;
}; // class ValuesInIteratorRangeGenerator
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// Stores a parameter value and later creates tests parameterized with that
// value.
template <class TestClass>
class ParameterizedTestFactory : public TestFactoryBase {
public:
typedef typename TestClass::ParamType ParamType;
explicit ParameterizedTestFactory(ParamType parameter) :
parameter_(parameter) {}
virtual Test* CreateTest() {
TestClass::SetParam(&parameter_);
return new TestClass();
}
private:
const ParamType parameter_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory);
};
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// TestMetaFactoryBase is a base class for meta-factories that create
// test factories for passing into MakeAndRegisterTestInfo function.
template <class ParamType>
class TestMetaFactoryBase {
public:
virtual ~TestMetaFactoryBase() {}
virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0;
};
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// 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
// it for each Test/Parameter value combination. Thus it needs meta factory
// creator class.
template <class TestCase>
class TestMetaFactory
: public TestMetaFactoryBase<typename TestCase::ParamType> {
public:
typedef typename TestCase::ParamType ParamType;
TestMetaFactory() {}
virtual TestFactoryBase* CreateTestFactory(ParamType parameter) {
return new ParameterizedTestFactory<TestCase>(parameter);
}
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory);
};
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// ParameterizedTestCaseInfoBase is a generic interface
// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase
// accumulates test information provided by TEST_P macro invocations
// and generators provided by INSTANTIATE_TEST_CASE_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
// and calls RegisterTests() on each of them when asked.
class ParameterizedTestCaseInfoBase {
public:
virtual ~ParameterizedTestCaseInfoBase() {}
// 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;
// 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.
virtual void RegisterTests() = 0;
protected:
ParameterizedTestCaseInfoBase() {}
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase);
};
// 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
// generators when asked.
template <class TestCase>
class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase {
public:
// ParamType and GeneratorCreationFunc are private types but are required
// for declarations of public methods AddTestPattern() and
// AddTestCaseInstantiation().
typedef typename TestCase::ParamType ParamType;
// A function that returns an instance of appropriate generator type.
typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
explicit ParameterizedTestCaseInfo(const char* name)
: test_case_name_(name) {}
// 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_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
// 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)));
}
// INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information
// about a generator.
int AddTestCaseInstantiation(const string& instantiation_name,
GeneratorCreationFunc* func,
const char* /* file */,
int /* line */) {
instantiations_.push_back(::std::make_pair(instantiation_name, func));
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() {
for (typename TestInfoContainer::iterator test_it = tests_.begin();
test_it != tests_.end(); ++test_it) {
linked_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->first;
ParamGenerator<ParamType> generator((*gen_it->second)());
string test_case_name;
if ( !instantiation_name.empty() )
test_case_name = instantiation_name + "/";
test_case_name += test_info->test_case_base_name;
int i = 0;
for (typename ParamGenerator<ParamType>::iterator param_it =
generator.begin();
param_it != generator.end(); ++param_it, ++i) {
Message test_name_stream;
test_name_stream << test_info->test_base_name << "/" << i;
MakeAndRegisterTestInfo(
test_case_name.c_str(),
test_name_stream.GetString().c_str(),
NULL, // No type parameter.
PrintToString(*param_it).c_str(),
GetTestCaseTypeId(),
TestCase::SetUpTestCase,
TestCase::TearDownTestCase,
test_info->test_meta_factory->CreateTestFactory(*param_it));
} // for param_it
} // for gen_it
} // for test_it
} // 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;
};
typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer;
// Keeps pairs of <Instantiation name, Sequence generator creation function>
// received from INSTANTIATE_TEST_CASE_P macros.
typedef ::std::vector<std::pair<string, GeneratorCreationFunc*> >
InstantiationContainer;
const string test_case_name_;
TestInfoContainer tests_;
InstantiationContainer instantiations_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo);
}; // class ParameterizedTestCaseInfo
// 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 {
public:
ParameterizedTestCaseRegistry() {}
~ParameterizedTestCaseRegistry() {
for (TestCaseInfoContainer::iterator it = test_case_infos_.begin();
it != test_case_infos_.end(); ++it) {
delete *it;
}
}
// 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,
const char* file,
int line) {
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>()) {
// 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, file, line);
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);
}
break;
}
}
if (typed_test_info == NULL) {
typed_test_info = new ParameterizedTestCaseInfo<TestCase>(test_case_name);
test_case_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();
}
}
private:
typedef ::std::vector<ParameterizedTestCaseInfoBase*> TestCaseInfoContainer;
TestCaseInfoContainer test_case_infos_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry);
};
} // namespace internal
} // namespace testing
#endif // GTEST_HAS_PARAM_TEST
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,167 @@
// Copyright 2005, 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.
//
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
//
// The Google C++ Testing 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>.
// It should not be #included by other files.
#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
#ifdef __BORLANDC__
// string.h is not guaranteed to provide strcpy on C++ Builder.
# include <mem.h>
#endif
#include <string.h>
#include <string>
#include "gtest/internal/gtest-port.h"
namespace testing {
namespace internal {
// String - an abstract class holding static string utilities.
class GTEST_API_ String {
public:
// Static utility methods
// Clones a 0-terminated C string, allocating memory using new. The
// caller is responsible for deleting the return value using
// delete[]. Returns the cloned string, or NULL if the input is
// NULL.
//
// This is different from strdup() in string.h, which allocates
// memory using malloc().
static const char* CloneCString(const char* c_str);
#if GTEST_OS_WINDOWS_MOBILE
// Windows CE does not have the 'ANSI' versions of Win32 APIs. To be
// able to pass strings to Win32 APIs on CE we need to convert them
// to 'Unicode', UTF-16.
// Creates a UTF-16 wide string from the given ANSI string, allocating
// memory using new. The caller is responsible for deleting the return
// value using delete[]. Returns the wide string, or NULL if the
// input is NULL.
//
// The wide string is created using the ANSI codepage (CP_ACP) to
// match the behaviour of the ANSI versions of Win32 calls and the
// C runtime.
static LPCWSTR AnsiToUtf16(const char* c_str);
// Creates an ANSI string from the given wide string, allocating
// memory using new. The caller is responsible for deleting the return
// value using delete[]. Returns the ANSI string, or NULL if the
// input is NULL.
//
// The returned string is created using the ANSI codepage (CP_ACP) to
// match the behaviour of the ANSI versions of Win32 calls and the
// C runtime.
static const char* Utf16ToAnsi(LPCWSTR utf16_str);
#endif
// Compares two C strings. Returns true iff 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,
// including the empty string.
static bool CStringEquals(const char* lhs, const char* rhs);
// Converts a wide C string to a String using the UTF-8 encoding.
// NULL will be converted to "(null)". If an error occurred during
// the conversion, "(failed to convert from wide string)" is
// returned.
static std::string ShowWideCString(const wchar_t* wide_c_str);
// Compares two wide C strings. Returns true iff 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.
//
// Unlike strcasecmp(), 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 CaseInsensitiveCStringEquals(const char* lhs,
const char* rhs);
// Compares two wide C strings, ignoring case. Returns true iff 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,
// including the empty string.
// NB: The implementations on different platforms slightly differ.
// On windows, this method uses _wcsicmp which compares according to LC_CTYPE
// environment variable. On GNU platform this method uses wcscasecmp
// which compares according to LC_CTYPE category of the current locale.
// On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
// current locale.
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.
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 as "%X".
static std::string FormatHexInt(int value);
// Formats a byte as "%02X".
static std::string FormatByte(unsigned char value);
private:
String(); // Not meant to be instantiated.
}; // class String
// Gets the content of the stringstream's buffer as an std::string. Each '\0'
// character in the buffer is replaced with "\\0".
GTEST_API_ std::string StringStreamToString(::std::stringstream* stream);
} // namespace internal
} // namespace testing
#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

48
extern/gtest/src/gtest-all.cc vendored Normal file
View File

@ -0,0 +1,48 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: mheule@google.com (Markus Heule)
//
// Google C++ Testing Framework (Google Test)
//
// Sometimes it's desirable to build Google Test by compiling a single file.
// This file serves this purpose.
// This line ensures that gtest.h can be compiled on its own, even
// when it's fused.
#include "gtest/gtest.h"
// The following lines pull in the real gtest *.cc files.
#include "src/gtest.cc"
#include "src/gtest-death-test.cc"
#include "src/gtest-filepath.cc"
#include "src/gtest-port.cc"
#include "src/gtest-printers.cc"
#include "src/gtest-test-part.cc"
#include "src/gtest-typed-test.cc"

1344
extern/gtest/src/gtest-death-test.cc vendored Normal file

File diff suppressed because it is too large Load Diff

382
extern/gtest/src/gtest-filepath.cc vendored Normal file
View File

@ -0,0 +1,382 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: keith.ray@gmail.com (Keith Ray)
#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-filepath.h"
#include "gtest/internal/gtest-port.h"
#include <stdlib.h>
#if GTEST_OS_WINDOWS_MOBILE
# include <windows.h>
#elif GTEST_OS_WINDOWS
# include <direct.h>
# include <io.h>
#elif GTEST_OS_SYMBIAN
// Symbian OpenC has PATH_MAX in sys/syslimits.h
# include <sys/syslimits.h>
#else
# include <limits.h>
# include <climits> // Some Linux distributions define PATH_MAX here.
#endif // GTEST_OS_WINDOWS_MOBILE
#if GTEST_OS_WINDOWS
# define GTEST_PATH_MAX_ _MAX_PATH
#elif defined(PATH_MAX)
# define GTEST_PATH_MAX_ PATH_MAX
#elif defined(_XOPEN_PATH_MAX)
# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
#else
# define GTEST_PATH_MAX_ _POSIX_PATH_MAX
#endif // GTEST_OS_WINDOWS
#include "gtest/internal/gtest-string.h"
namespace testing {
namespace internal {
#if GTEST_OS_WINDOWS
// On Windows, '\\' is the standard path separator, but many tools and the
// Windows API also accept '/' as an alternate path separator. Unless otherwise
// noted, a file path can contain either kind of path separators, or a mixture
// of them.
const char kPathSeparator = '\\';
const char kAlternatePathSeparator = '/';
const char kPathSeparatorString[] = "\\";
const char kAlternatePathSeparatorString[] = "/";
# if GTEST_OS_WINDOWS_MOBILE
// Windows CE doesn't have a current directory. You should not use
// the current directory in tests on Windows CE, but this at least
// provides a reasonable fallback.
const char kCurrentDirectoryString[] = "\\";
// Windows CE doesn't define INVALID_FILE_ATTRIBUTES
const DWORD kInvalidFileAttributes = 0xffffffff;
# else
const char kCurrentDirectoryString[] = ".\\";
# endif // GTEST_OS_WINDOWS_MOBILE
#else
const char kPathSeparator = '/';
const char kPathSeparatorString[] = "/";
const char kCurrentDirectoryString[] = "./";
#endif // GTEST_OS_WINDOWS
// Returns whether the given character is a valid path separator.
static bool IsPathSeparator(char c) {
#if GTEST_HAS_ALT_PATH_SEP_
return (c == kPathSeparator) || (c == kAlternatePathSeparator);
#else
return c == kPathSeparator;
#endif
}
// Returns the current working directory, or "" if unsuccessful.
FilePath FilePath::GetCurrentDir() {
#if GTEST_OS_WINDOWS_MOBILE
// Windows CE doesn't have a current directory, so we just return
// something reasonable.
return FilePath(kCurrentDirectoryString);
#elif GTEST_OS_WINDOWS
char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
#else
char cwd[GTEST_PATH_MAX_ + 1] = { '\0' };
return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd);
#endif // GTEST_OS_WINDOWS_MOBILE
}
// Returns a copy of the FilePath with the case-insensitive extension removed.
// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
// FilePath("dir/file"). If a case-insensitive extension is not
// found, returns a copy of the original FilePath.
FilePath FilePath::RemoveExtension(const char* extension) const {
const std::string dot_extension = std::string(".") + extension;
if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) {
return FilePath(pathname_.substr(
0, pathname_.length() - dot_extension.length()));
}
return *this;
}
// Returns a pointer to the last occurence 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* FilePath::FindLastPathSeparator() const {
const char* const last_sep = strrchr(c_str(), kPathSeparator);
#if GTEST_HAS_ALT_PATH_SEP_
const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
// Comparing two pointers of which only one is NULL is undefined.
if (last_alt_sep != NULL &&
(last_sep == NULL || last_alt_sep > last_sep)) {
return last_alt_sep;
}
#endif
return last_sep;
}
// Returns a copy of the FilePath with the directory part removed.
// Example: FilePath("path/to/file").RemoveDirectoryName() returns
// FilePath("file"). If there is no directory part ("just_a_file"), it returns
// the FilePath unmodified. If there is no file part ("just_a_dir/") it
// returns an empty FilePath ("").
// On Windows platform, '\' is the path separator, otherwise it is '/'.
FilePath FilePath::RemoveDirectoryName() const {
const char* const last_sep = FindLastPathSeparator();
return last_sep ? FilePath(last_sep + 1) : *this;
}
// RemoveFileName returns the directory path with the filename removed.
// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
// On Windows platform, '\' is the path separator, otherwise it is '/'.
FilePath FilePath::RemoveFileName() const {
const char* const last_sep = FindLastPathSeparator();
std::string dir;
if (last_sep) {
dir = std::string(c_str(), last_sep + 1 - c_str());
} else {
dir = kCurrentDirectoryString;
}
return FilePath(dir);
}
// Helper functions for naming files in a directory for xml output.
// Given directory = "dir", base_name = "test", number = 0,
// extension = "xml", returns "dir/test.xml". If number is greater
// than zero (e.g., 12), returns "dir/test_12.xml".
// On Windows platform, uses \ as the separator rather than /.
FilePath FilePath::MakeFileName(const FilePath& directory,
const FilePath& base_name,
int number,
const char* extension) {
std::string file;
if (number == 0) {
file = base_name.string() + "." + extension;
} else {
file = base_name.string() + "_" + StreamableToString(number)
+ "." + extension;
}
return ConcatPaths(directory, FilePath(file));
}
// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
// On Windows, uses \ as the separator rather than /.
FilePath FilePath::ConcatPaths(const FilePath& directory,
const FilePath& relative_path) {
if (directory.IsEmpty())
return relative_path;
const FilePath dir(directory.RemoveTrailingPathSeparator());
return FilePath(dir.string() + kPathSeparator + relative_path.string());
}
// Returns true if pathname describes something findable in the file-system,
// either a file, directory, or whatever.
bool FilePath::FileOrDirectoryExists() const {
#if GTEST_OS_WINDOWS_MOBILE
LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
const DWORD attributes = GetFileAttributes(unicode);
delete [] unicode;
return attributes != kInvalidFileAttributes;
#else
posix::StatStruct file_stat;
return posix::Stat(pathname_.c_str(), &file_stat) == 0;
#endif // GTEST_OS_WINDOWS_MOBILE
}
// Returns true if pathname describes a directory in the file-system
// that exists.
bool FilePath::DirectoryExists() const {
bool result = false;
#if GTEST_OS_WINDOWS
// Don't strip off trailing separator if path is a root directory on
// Windows (like "C:\\").
const FilePath& path(IsRootDirectory() ? *this :
RemoveTrailingPathSeparator());
#else
const FilePath& path(*this);
#endif
#if GTEST_OS_WINDOWS_MOBILE
LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
const DWORD attributes = GetFileAttributes(unicode);
delete [] unicode;
if ((attributes != kInvalidFileAttributes) &&
(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
result = true;
}
#else
posix::StatStruct file_stat;
result = posix::Stat(path.c_str(), &file_stat) == 0 &&
posix::IsDir(file_stat);
#endif // GTEST_OS_WINDOWS_MOBILE
return result;
}
// Returns true if pathname describes a root directory. (Windows has one
// root directory per disk drive.)
bool FilePath::IsRootDirectory() const {
#if GTEST_OS_WINDOWS
// TODO(wan@google.com): on Windows a network share like
// \\server\share can be a root directory, although it cannot be the
// current directory. Handle this properly.
return pathname_.length() == 3 && IsAbsolutePath();
#else
return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]);
#endif
}
// Returns true if pathname describes an absolute path.
bool FilePath::IsAbsolutePath() const {
const char* const name = pathname_.c_str();
#if GTEST_OS_WINDOWS
return pathname_.length() >= 3 &&
((name[0] >= 'a' && name[0] <= 'z') ||
(name[0] >= 'A' && name[0] <= 'Z')) &&
name[1] == ':' &&
IsPathSeparator(name[2]);
#else
return IsPathSeparator(name[0]);
#endif
}
// Returns a pathname for a file that does not currently exist. The pathname
// will be directory/base_name.extension or
// directory/base_name_<number>.extension if directory/base_name.extension
// already exists. The number will be incremented until a pathname is found
// that does not already exist.
// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
// There could be a race condition if two or more processes are calling this
// function at the same time -- they could both pick the same filename.
FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
const FilePath& base_name,
const char* extension) {
FilePath full_pathname;
int number = 0;
do {
full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
} while (full_pathname.FileOrDirectoryExists());
return full_pathname;
}
// Returns true if FilePath ends with a path separator, which indicates that
// it is intended to represent a directory. Returns false otherwise.
// This does NOT check that a directory (or file) actually exists.
bool FilePath::IsDirectory() const {
return !pathname_.empty() &&
IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
}
// Create directories so that path exists. Returns true if successful or if
// the directories already exist; returns false if unable to create directories
// for any reason.
bool FilePath::CreateDirectoriesRecursively() const {
if (!this->IsDirectory()) {
return false;
}
if (pathname_.length() == 0 || this->DirectoryExists()) {
return true;
}
const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
return parent.CreateDirectoriesRecursively() && this->CreateFolder();
}
// Create the directory so that path exists. Returns true if successful or
// if the directory already exists; returns false if unable to create the
// directory for any reason, including if the parent directory does not
// exist. Not named "CreateDirectory" because that's a macro on Windows.
bool FilePath::CreateFolder() const {
#if GTEST_OS_WINDOWS_MOBILE
FilePath removed_sep(this->RemoveTrailingPathSeparator());
LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
int result = CreateDirectory(unicode, NULL) ? 0 : -1;
delete [] unicode;
#elif GTEST_OS_WINDOWS
int result = _mkdir(pathname_.c_str());
#else
int result = mkdir(pathname_.c_str(), 0777);
#endif // GTEST_OS_WINDOWS_MOBILE
if (result == -1) {
return this->DirectoryExists(); // An error is OK if the directory exists.
}
return true; // No error.
}
// If input name has a trailing separator character, remove it and return the
// name, otherwise return the name string unmodified.
// On Windows platform, uses \ as the separator, other platforms use /.
FilePath FilePath::RemoveTrailingPathSeparator() const {
return IsDirectory()
? FilePath(pathname_.substr(0, pathname_.length() - 1))
: *this;
}
// Removes any redundant separators that might be in the pathname.
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
// redundancies that might be in a pathname involving "." or "..".
// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share).
void FilePath::Normalize() {
if (pathname_.c_str() == NULL) {
pathname_ = "";
return;
}
const char* src = pathname_.c_str();
char* const dest = new char[pathname_.length() + 1];
char* dest_ptr = dest;
memset(dest_ptr, 0, pathname_.length() + 1);
while (*src != '\0') {
*dest_ptr = *src;
if (!IsPathSeparator(*src)) {
src++;
} else {
#if GTEST_HAS_ALT_PATH_SEP_
if (*dest_ptr == kAlternatePathSeparator) {
*dest_ptr = kPathSeparator;
}
#endif
while (IsPathSeparator(*src))
src++;
}
dest_ptr++;
}
*dest_ptr = '\0';
pathname_ = dest;
delete[] dest;
}
} // namespace internal
} // namespace testing

1218
extern/gtest/src/gtest-internal-inl.h vendored Normal file

File diff suppressed because it is too large Load Diff

805
extern/gtest/src/gtest-port.cc vendored Normal file
View File

@ -0,0 +1,805 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
#include "gtest/internal/gtest-port.h"
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if GTEST_OS_WINDOWS_MOBILE
# include <windows.h> // For TerminateProcess()
#elif GTEST_OS_WINDOWS
# include <io.h>
# include <sys/stat.h>
#else
# include <unistd.h>
#endif // GTEST_OS_WINDOWS_MOBILE
#if GTEST_OS_MAC
# include <mach/mach_init.h>
# include <mach/task.h>
# include <mach/vm_map.h>
#endif // GTEST_OS_MAC
#if GTEST_OS_QNX
# include <devctl.h>
# include <sys/procfs.h>
#endif // GTEST_OS_QNX
#include "gtest/gtest-spi.h"
#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-string.h"
// Indicates that this translation unit is part of Google Test's
// implementation. It must come before gtest-internal-inl.h is
// included, or there will be a compiler error. This trick is to
// prevent a user from accidentally including gtest-internal-inl.h in
// his code.
#define GTEST_IMPLEMENTATION_ 1
#include "src/gtest-internal-inl.h"
#undef GTEST_IMPLEMENTATION_
namespace testing {
namespace internal {
#if defined(_MSC_VER) || defined(__BORLANDC__)
// MSVC and C++Builder do not provide a definition of STDERR_FILENO.
const int kStdOutFileno = 1;
const int kStdErrFileno = 2;
#else
const int kStdOutFileno = STDOUT_FILENO;
const int kStdErrFileno = STDERR_FILENO;
#endif // _MSC_VER
#if GTEST_OS_MAC
// Returns the number of threads running in the process, or 0 to indicate that
// we cannot detect it.
size_t GetThreadCount() {
const task_t task = mach_task_self();
mach_msg_type_number_t thread_count;
thread_act_array_t thread_list;
const kern_return_t status = task_threads(task, &thread_list, &thread_count);
if (status == KERN_SUCCESS) {
// task_threads allocates resources in thread_list and we need to free them
// to avoid leaks.
vm_deallocate(task,
reinterpret_cast<vm_address_t>(thread_list),
sizeof(thread_t) * thread_count);
return static_cast<size_t>(thread_count);
} else {
return 0;
}
}
#elif GTEST_OS_QNX
// Returns the number of threads running in the process, or 0 to indicate that
// we cannot detect it.
size_t GetThreadCount() {
const int fd = open("/proc/self/as", O_RDONLY);
if (fd < 0) {
return 0;
}
procfs_info process_info;
const int status =
devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL);
close(fd);
if (status == EOK) {
return static_cast<size_t>(process_info.num_threads);
} else {
return 0;
}
}
#else
size_t GetThreadCount() {
// There's no portable way to detect the number of threads, so we just
// return 0 to indicate that we cannot detect it.
return 0;
}
#endif // GTEST_OS_MAC
#if GTEST_USES_POSIX_RE
// Implements RE. Currently only needed for death tests.
RE::~RE() {
if (is_valid_) {
// regfree'ing an invalid regex might crash because the content
// of the regex is undefined. Since the regex's are essentially
// the same, one cannot be valid (or invalid) without the other
// being so too.
regfree(&partial_regex_);
regfree(&full_regex_);
}
free(const_cast<char*>(pattern_));
}
// Returns true iff regular expression re matches the entire str.
bool RE::FullMatch(const char* str, const RE& re) {
if (!re.is_valid_) return false;
regmatch_t match;
return regexec(&re.full_regex_, str, 1, &match, 0) == 0;
}
// Returns true iff regular expression re matches a substring of str
// (including str itself).
bool RE::PartialMatch(const char* str, const RE& re) {
if (!re.is_valid_) return false;
regmatch_t match;
return regexec(&re.partial_regex_, str, 1, &match, 0) == 0;
}
// Initializes an RE from its string representation.
void RE::Init(const char* regex) {
pattern_ = posix::StrDup(regex);
// Reserves enough bytes to hold the regular expression used for a
// full match.
const size_t full_regex_len = strlen(regex) + 10;
char* const full_pattern = new char[full_regex_len];
snprintf(full_pattern, full_regex_len, "^(%s)$", regex);
is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0;
// We want to call regcomp(&partial_regex_, ...) even if the
// previous expression returns false. Otherwise partial_regex_ may
// not be properly initialized can may cause trouble when it's
// freed.
//
// Some implementation of POSIX regex (e.g. on at least some
// versions of Cygwin) doesn't accept the empty string as a valid
// regex. We change it to an equivalent form "()" to be safe.
if (is_valid_) {
const char* const partial_regex = (*regex == '\0') ? "()" : regex;
is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0;
}
EXPECT_TRUE(is_valid_)
<< "Regular expression \"" << regex
<< "\" is not a valid POSIX Extended regular expression.";
delete[] full_pattern;
}
#elif GTEST_USES_SIMPLE_RE
// Returns true iff ch appears anywhere in str (excluding the
// terminating '\0' character).
bool IsInSet(char ch, const char* str) {
return ch != '\0' && strchr(str, ch) != NULL;
}
// Returns true iff ch belongs to the given classification. Unlike
// similar functions in <ctype.h>, these aren't affected by the
// current locale.
bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; }
bool IsAsciiPunct(char ch) {
return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~");
}
bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); }
bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); }
bool IsAsciiWordChar(char ch) {
return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') ||
('0' <= ch && ch <= '9') || ch == '_';
}
// Returns true iff "\\c" is a supported escape sequence.
bool IsValidEscape(char c) {
return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW"));
}
// Returns true iff the given atom (specified by escaped and pattern)
// matches ch. The result is undefined if the atom is invalid.
bool AtomMatchesChar(bool escaped, char pattern_char, char ch) {
if (escaped) { // "\\p" where p is pattern_char.
switch (pattern_char) {
case 'd': return IsAsciiDigit(ch);
case 'D': return !IsAsciiDigit(ch);
case 'f': return ch == '\f';
case 'n': return ch == '\n';
case 'r': return ch == '\r';
case 's': return IsAsciiWhiteSpace(ch);
case 'S': return !IsAsciiWhiteSpace(ch);
case 't': return ch == '\t';
case 'v': return ch == '\v';
case 'w': return IsAsciiWordChar(ch);
case 'W': return !IsAsciiWordChar(ch);
}
return IsAsciiPunct(pattern_char) && pattern_char == ch;
}
return (pattern_char == '.' && ch != '\n') || pattern_char == ch;
}
// Helper function used by ValidateRegex() to format error messages.
std::string FormatRegexSyntaxError(const char* regex, int index) {
return (Message() << "Syntax error at index " << index
<< " in simple regular expression \"" << regex << "\": ").GetString();
}
// Generates non-fatal failures and returns false if regex is invalid;
// otherwise returns true.
bool ValidateRegex(const char* regex) {
if (regex == NULL) {
// TODO(wan@google.com): fix the source file location in the
// assertion failures to match where the regex is used in user
// code.
ADD_FAILURE() << "NULL is not a valid simple regular expression.";
return false;
}
bool is_valid = true;
// True iff ?, *, or + can follow the previous atom.
bool prev_repeatable = false;
for (int i = 0; regex[i]; i++) {
if (regex[i] == '\\') { // An escape sequence
i++;
if (regex[i] == '\0') {
ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1)
<< "'\\' cannot appear at the end.";
return false;
}
if (!IsValidEscape(regex[i])) {
ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1)
<< "invalid escape sequence \"\\" << regex[i] << "\".";
is_valid = false;
}
prev_repeatable = true;
} else { // Not an escape sequence.
const char ch = regex[i];
if (ch == '^' && i > 0) {
ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
<< "'^' can only appear at the beginning.";
is_valid = false;
} else if (ch == '$' && regex[i + 1] != '\0') {
ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
<< "'$' can only appear at the end.";
is_valid = false;
} else if (IsInSet(ch, "()[]{}|")) {
ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
<< "'" << ch << "' is unsupported.";
is_valid = false;
} else if (IsRepeat(ch) && !prev_repeatable) {
ADD_FAILURE() << FormatRegexSyntaxError(regex, i)
<< "'" << ch << "' can only follow a repeatable token.";
is_valid = false;
}
prev_repeatable = !IsInSet(ch, "^$?*+");
}
}
return is_valid;
}
// Matches a repeated regex atom followed by a valid simple regular
// expression. The regex atom is defined as c if escaped is false,
// or \c otherwise. repeat is the repetition meta character (?, *,
// or +). The behavior is undefined if str contains too many
// characters to be indexable by size_t, in which case the test will
// probably time out anyway. We are fine with this limitation as
// std::string has it too.
bool MatchRepetitionAndRegexAtHead(
bool escaped, char c, char repeat, const char* regex,
const char* str) {
const size_t min_count = (repeat == '+') ? 1 : 0;
const size_t max_count = (repeat == '?') ? 1 :
static_cast<size_t>(-1) - 1;
// We cannot call numeric_limits::max() as it conflicts with the
// max() macro on Windows.
for (size_t i = 0; i <= max_count; ++i) {
// We know that the atom matches each of the first i characters in str.
if (i >= min_count && MatchRegexAtHead(regex, str + i)) {
// We have enough matches at the head, and the tail matches too.
// Since we only care about *whether* the pattern matches str
// (as opposed to *how* it matches), there is no need to find a
// greedy match.
return true;
}
if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i]))
return false;
}
return false;
}
// Returns true iff regex matches a prefix of str. regex must be a
// valid simple regular expression and not start with "^", or the
// result is undefined.
bool MatchRegexAtHead(const char* regex, const char* str) {
if (*regex == '\0') // An empty regex matches a prefix of anything.
return true;
// "$" only matches the end of a string. Note that regex being
// valid guarantees that there's nothing after "$" in it.
if (*regex == '$')
return *str == '\0';
// Is the first thing in regex an escape sequence?
const bool escaped = *regex == '\\';
if (escaped)
++regex;
if (IsRepeat(regex[1])) {
// MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so
// here's an indirect recursion. It terminates as the regex gets
// shorter in each recursion.
return MatchRepetitionAndRegexAtHead(
escaped, regex[0], regex[1], regex + 2, str);
} else {
// regex isn't empty, isn't "$", and doesn't start with a
// repetition. We match the first atom of regex with the first
// character of str and recurse.
return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) &&
MatchRegexAtHead(regex + 1, str + 1);
}
}
// Returns true iff regex matches any substring of str. regex must be
// a valid simple regular expression, or the result is undefined.
//
// The algorithm is recursive, but the recursion depth doesn't exceed
// the regex length, so we won't need to worry about running out of
// stack space normally. In rare cases the time complexity can be
// exponential with respect to the regex length + the string length,
// but usually it's must faster (often close to linear).
bool MatchRegexAnywhere(const char* regex, const char* str) {
if (regex == NULL || str == NULL)
return false;
if (*regex == '^')
return MatchRegexAtHead(regex + 1, str);
// A successful match can be anywhere in str.
do {
if (MatchRegexAtHead(regex, str))
return true;
} while (*str++ != '\0');
return false;
}
// Implements the RE class.
RE::~RE() {
free(const_cast<char*>(pattern_));
free(const_cast<char*>(full_pattern_));
}
// Returns true iff regular expression re matches the entire str.
bool RE::FullMatch(const char* str, const RE& re) {
return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str);
}
// Returns true iff regular expression re matches a substring of str
// (including str itself).
bool RE::PartialMatch(const char* str, const RE& re) {
return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str);
}
// Initializes an RE from its string representation.
void RE::Init(const char* regex) {
pattern_ = full_pattern_ = NULL;
if (regex != NULL) {
pattern_ = posix::StrDup(regex);
}
is_valid_ = ValidateRegex(regex);
if (!is_valid_) {
// No need to calculate the full pattern when the regex is invalid.
return;
}
const size_t len = strlen(regex);
// Reserves enough bytes to hold the regular expression used for a
// full match: we need space to prepend a '^', append a '$', and
// terminate the string with '\0'.
char* buffer = static_cast<char*>(malloc(len + 3));
full_pattern_ = buffer;
if (*regex != '^')
*buffer++ = '^'; // Makes sure full_pattern_ starts with '^'.
// We don't use snprintf or strncpy, as they trigger a warning when
// compiled with VC++ 8.0.
memcpy(buffer, regex, len);
buffer += len;
if (len == 0 || regex[len - 1] != '$')
*buffer++ = '$'; // Makes sure full_pattern_ ends with '$'.
*buffer = '\0';
}
#endif // GTEST_USES_POSIX_RE
const char kUnknownFile[] = "unknown file";
// Formats a source file path and a line number as they would appear
// in an error message from the compiler used to compile this code.
GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) {
const std::string file_name(file == NULL ? kUnknownFile : file);
if (line < 0) {
return file_name + ":";
}
#ifdef _MSC_VER
return file_name + "(" + StreamableToString(line) + "):";
#else
return file_name + ":" + StreamableToString(line) + ":";
#endif // _MSC_VER
}
// Formats a file location for compiler-independent XML output.
// Although this function is not platform dependent, we put it next to
// FormatFileLocation in order to contrast the two functions.
// Note that FormatCompilerIndependentFileLocation() does NOT append colon
// to the file location it produces, unlike FormatFileLocation().
GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(
const char* file, int line) {
const std::string file_name(file == NULL ? kUnknownFile : file);
if (line < 0)
return file_name;
else
return file_name + ":" + StreamableToString(line);
}
GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line)
: severity_(severity) {
const char* const marker =
severity == GTEST_INFO ? "[ INFO ]" :
severity == GTEST_WARNING ? "[WARNING]" :
severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]";
GetStream() << ::std::endl << marker << " "
<< FormatFileLocation(file, line).c_str() << ": ";
}
// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program.
GTestLog::~GTestLog() {
GetStream() << ::std::endl;
if (severity_ == GTEST_FATAL) {
fflush(stderr);
posix::Abort();
}
}
// Disable Microsoft deprecation warnings for POSIX functions called from
// this class (creat, dup, dup2, and close)
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4996)
#endif // _MSC_VER
#if GTEST_HAS_STREAM_REDIRECTION
// Object that captures an output stream (stdout/stderr).
class CapturedStream {
public:
// The ctor redirects the stream to a temporary file.
explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) {
# if GTEST_OS_WINDOWS
char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT
char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT
::GetTempPathA(sizeof(temp_dir_path), temp_dir_path);
const UINT success = ::GetTempFileNameA(temp_dir_path,
"gtest_redir",
0, // Generate unique file name.
temp_file_path);
GTEST_CHECK_(success != 0)
<< "Unable to create a temporary file in " << temp_dir_path;
const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE);
GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file "
<< temp_file_path;
filename_ = temp_file_path;
# else
// There's no guarantee that a test has write access to the current
// directory, so we create the temporary file in the /tmp directory
// instead. We use /tmp on most systems, and /sdcard on Android.
// That's because Android doesn't have /tmp.
# if GTEST_OS_LINUX_ANDROID
// Note: Android applications are expected to call the framework's
// Context.getExternalStorageDirectory() method through JNI to get
// the location of the world-writable SD Card directory. However,
// this requires a Context handle, which cannot be retrieved
// globally from native code. Doing so also precludes running the
// code as part of a regular standalone executable, which doesn't
// run in a Dalvik process (e.g. when running it through 'adb shell').
//
// The location /sdcard is directly accessible from native code
// and is the only location (unofficially) supported by the Android
// team. It's generally a symlink to the real SD Card mount point
// which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or
// other OEM-customized locations. Never rely on these, and always
// use /sdcard.
char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX";
# else
char name_template[] = "/tmp/captured_stream.XXXXXX";
# endif // GTEST_OS_LINUX_ANDROID
const int captured_fd = mkstemp(name_template);
filename_ = name_template;
# endif // GTEST_OS_WINDOWS
fflush(NULL);
dup2(captured_fd, fd_);
close(captured_fd);
}
~CapturedStream() {
remove(filename_.c_str());
}
std::string GetCapturedString() {
if (uncaptured_fd_ != -1) {
// Restores the original stream.
fflush(NULL);
dup2(uncaptured_fd_, fd_);
close(uncaptured_fd_);
uncaptured_fd_ = -1;
}
FILE* const file = posix::FOpen(filename_.c_str(), "r");
const std::string content = ReadEntireFile(file);
posix::FClose(file);
return content;
}
private:
// Reads the entire content of a file as an std::string.
static std::string ReadEntireFile(FILE* file);
// Returns the size (in bytes) of a file.
static size_t GetFileSize(FILE* file);
const int fd_; // A stream to capture.
int uncaptured_fd_;
// Name of the temporary file holding the stderr output.
::std::string filename_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream);
};
// Returns the size (in bytes) of a file.
size_t CapturedStream::GetFileSize(FILE* file) {
fseek(file, 0, SEEK_END);
return static_cast<size_t>(ftell(file));
}
// Reads the entire content of a file as a string.
std::string CapturedStream::ReadEntireFile(FILE* file) {
const size_t file_size = GetFileSize(file);
char* const buffer = new char[file_size];
size_t bytes_last_read = 0; // # of bytes read in the last fread()
size_t bytes_read = 0; // # of bytes read so far
fseek(file, 0, SEEK_SET);
// Keeps reading the file until we cannot read further or the
// pre-determined file size is reached.
do {
bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file);
bytes_read += bytes_last_read;
} while (bytes_last_read > 0 && bytes_read < file_size);
const std::string content(buffer, bytes_read);
delete[] buffer;
return content;
}
# ifdef _MSC_VER
# pragma warning(pop)
# endif // _MSC_VER
static CapturedStream* g_captured_stderr = NULL;
static CapturedStream* g_captured_stdout = NULL;
// Starts capturing an output stream (stdout/stderr).
void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) {
if (*stream != NULL) {
GTEST_LOG_(FATAL) << "Only one " << stream_name
<< " capturer can exist at a time.";
}
*stream = new CapturedStream(fd);
}
// Stops capturing the output stream and returns the captured string.
std::string GetCapturedStream(CapturedStream** captured_stream) {
const std::string content = (*captured_stream)->GetCapturedString();
delete *captured_stream;
*captured_stream = NULL;
return content;
}
// Starts capturing stdout.
void CaptureStdout() {
CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout);
}
// Starts capturing stderr.
void CaptureStderr() {
CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr);
}
// Stops capturing stdout and returns the captured string.
std::string GetCapturedStdout() {
return GetCapturedStream(&g_captured_stdout);
}
// Stops capturing stderr and returns the captured string.
std::string GetCapturedStderr() {
return GetCapturedStream(&g_captured_stderr);
}
#endif // GTEST_HAS_STREAM_REDIRECTION
#if GTEST_HAS_DEATH_TEST
// A copy of all command line arguments. Set by InitGoogleTest().
::std::vector<testing::internal::string> g_argvs;
static const ::std::vector<testing::internal::string>* g_injected_test_argvs =
NULL; // Owned.
void SetInjectableArgvs(const ::std::vector<testing::internal::string>* argvs) {
if (g_injected_test_argvs != argvs)
delete g_injected_test_argvs;
g_injected_test_argvs = argvs;
}
const ::std::vector<testing::internal::string>& GetInjectableArgvs() {
if (g_injected_test_argvs != NULL) {
return *g_injected_test_argvs;
}
return g_argvs;
}
#endif // GTEST_HAS_DEATH_TEST
#if GTEST_OS_WINDOWS_MOBILE
namespace posix {
void Abort() {
DebugBreak();
TerminateProcess(GetCurrentProcess(), 1);
}
} // namespace posix
#endif // GTEST_OS_WINDOWS_MOBILE
// Returns the name of the environment variable corresponding to the
// given flag. For example, FlagToEnvVar("foo") will return
// "GTEST_FOO" in the open-source version.
static std::string FlagToEnvVar(const char* flag) {
const std::string full_flag =
(Message() << GTEST_FLAG_PREFIX_ << flag).GetString();
Message env_var;
for (size_t i = 0; i != full_flag.length(); i++) {
env_var << ToUpper(full_flag.c_str()[i]);
}
return env_var.GetString();
}
// Parses 'str' for a 32-bit signed integer. If successful, writes
// the result to *value and returns true; otherwise leaves *value
// unchanged and returns false.
bool ParseInt32(const Message& src_text, const char* str, Int32* value) {
// Parses the environment variable as a decimal integer.
char* end = NULL;
const long long_value = strtol(str, &end, 10); // NOLINT
// Has strtol() consumed all characters in the string?
if (*end != '\0') {
// No - an invalid character was encountered.
Message msg;
msg << "WARNING: " << src_text
<< " is expected to be a 32-bit integer, but actually"
<< " has value \"" << str << "\".\n";
printf("%s", msg.GetString().c_str());
fflush(stdout);
return false;
}
// Is the parsed value in the range of an Int32?
const Int32 result = static_cast<Int32>(long_value);
if (long_value == LONG_MAX || long_value == LONG_MIN ||
// The parsed value overflows as a long. (strtol() returns
// LONG_MAX or LONG_MIN when the input overflows.)
result != long_value
// The parsed value overflows as an Int32.
) {
Message msg;
msg << "WARNING: " << src_text
<< " is expected to be a 32-bit integer, but actually"
<< " has value " << str << ", which overflows.\n";
printf("%s", msg.GetString().c_str());
fflush(stdout);
return false;
}
*value = result;
return true;
}
// Reads and returns the Boolean environment variable corresponding to
// the given flag; if it's not set, returns default_value.
//
// The value is considered true iff it's not "0".
bool BoolFromGTestEnv(const char* flag, bool default_value) {
const std::string env_var = FlagToEnvVar(flag);
const char* const string_value = posix::GetEnv(env_var.c_str());
return string_value == NULL ?
default_value : strcmp(string_value, "0") != 0;
}
// Reads and returns a 32-bit integer stored in the environment
// variable corresponding to the given flag; if it isn't set or
// doesn't represent a valid 32-bit integer, returns default_value.
Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) {
const std::string env_var = FlagToEnvVar(flag);
const char* const string_value = posix::GetEnv(env_var.c_str());
if (string_value == NULL) {
// The environment variable is not set.
return default_value;
}
Int32 result = default_value;
if (!ParseInt32(Message() << "Environment variable " << env_var,
string_value, &result)) {
printf("The default value %s is used.\n",
(Message() << default_value).GetString().c_str());
fflush(stdout);
return default_value;
}
return result;
}
// Reads and returns the string environment variable corresponding to
// the given flag; if it's not set, returns default_value.
const char* StringFromGTestEnv(const char* flag, const char* default_value) {
const std::string env_var = FlagToEnvVar(flag);
const char* const value = posix::GetEnv(env_var.c_str());
return value == NULL ? default_value : value;
}
} // namespace internal
} // namespace testing

363
extern/gtest/src/gtest-printers.cc vendored Normal file
View File

@ -0,0 +1,363 @@
// Copyright 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
// Google Test - The Google C++ Testing Framework
//
// This file implements a universal value printer that can print a
// value of any type T:
//
// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
//
// It uses the << operator when possible, and prints the bytes in the
// object otherwise. A user can override its behavior for a class
// type Foo by defining either operator<<(::std::ostream&, const Foo&)
// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
// defines Foo.
#include "gtest/gtest-printers.h"
#include <ctype.h>
#include <stdio.h>
#include <ostream> // NOLINT
#include <string>
#include "gtest/internal/gtest-port.h"
namespace testing {
namespace {
using ::std::ostream;
// Prints a segment of bytes in the given object.
void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
size_t count, ostream* os) {
char text[5] = "";
for (size_t i = 0; i != count; i++) {
const size_t j = start + i;
if (i != 0) {
// Organizes the bytes into groups of 2 for easy parsing by
// human.
if ((j % 2) == 0)
*os << ' ';
else
*os << '-';
}
GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]);
*os << text;
}
}
// Prints the bytes in the given value to the given ostream.
void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
ostream* os) {
// Tells the user how big the object is.
*os << count << "-byte object <";
const size_t kThreshold = 132;
const size_t kChunkSize = 64;
// If the object size is bigger than kThreshold, we'll have to omit
// some details by printing only the first and the last kChunkSize
// bytes.
// TODO(wan): let the user control the threshold using a flag.
if (count < kThreshold) {
PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
} else {
PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os);
*os << " ... ";
// Rounds up to 2-byte boundary.
const size_t resume_pos = (count - kChunkSize + 1)/2*2;
PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os);
}
*os << ">";
}
} // namespace
namespace internal2 {
// Delegates to PrintBytesInObjectToImpl() to print the bytes in the
// given object. The delegation simplifies the implementation, which
// uses the << operator and thus is easier done outside of the
// ::testing::internal namespace, which contains a << operator that
// sometimes conflicts with the one in STL.
void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count,
ostream* os) {
PrintBytesInObjectToImpl(obj_bytes, count, os);
}
} // namespace internal2
namespace internal {
// Depending on the value of a char (or wchar_t), we print it in one
// of three formats:
// - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
// - as a hexidecimal escape sequence (e.g. '\x7F'), or
// - as a special escape sequence (e.g. '\r', '\n').
enum CharFormat {
kAsIs,
kHexEscape,
kSpecialEscape
};
// Returns true if c is a printable ASCII character. We test the
// value of c directly instead of calling isprint(), which is buggy on
// Windows Mobile.
inline bool IsPrintableAscii(wchar_t c) {
return 0x20 <= c && c <= 0x7E;
}
// Prints a wide or narrow char c as a character literal without the
// quotes, escaping it when necessary; returns how c was formatted.
// The template argument UnsignedChar is the unsigned version of Char,
// which is the type of c.
template <typename UnsignedChar, typename Char>
static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
switch (static_cast<wchar_t>(c)) {
case L'\0':
*os << "\\0";
break;
case L'\'':
*os << "\\'";
break;
case L'\\':
*os << "\\\\";
break;
case L'\a':
*os << "\\a";
break;
case L'\b':
*os << "\\b";
break;
case L'\f':
*os << "\\f";
break;
case L'\n':
*os << "\\n";
break;
case L'\r':
*os << "\\r";
break;
case L'\t':
*os << "\\t";
break;
case L'\v':
*os << "\\v";
break;
default:
if (IsPrintableAscii(c)) {
*os << static_cast<char>(c);
return kAsIs;
} else {
*os << "\\x" + String::FormatHexInt(static_cast<UnsignedChar>(c));
return kHexEscape;
}
}
return kSpecialEscape;
}
// Prints a wchar_t c as if it's part of a string literal, escaping it when
// necessary; returns how c was formatted.
static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) {
switch (c) {
case L'\'':
*os << "'";
return kAsIs;
case L'"':
*os << "\\\"";
return kSpecialEscape;
default:
return PrintAsCharLiteralTo<wchar_t>(c, os);
}
}
// Prints a char c as if it's part of a string literal, escaping it when
// necessary; returns how c was formatted.
static CharFormat PrintAsStringLiteralTo(char c, ostream* os) {
return PrintAsStringLiteralTo(
static_cast<wchar_t>(static_cast<unsigned char>(c)), os);
}
// Prints a wide or narrow character c and its code. '\0' is printed
// as "'\\0'", other unprintable characters are also properly escaped
// using the standard C++ escape sequence. The template argument
// UnsignedChar is the unsigned version of Char, which is the type of c.
template <typename UnsignedChar, typename Char>
void PrintCharAndCodeTo(Char c, ostream* os) {
// First, print c as a literal in the most readable form we can find.
*os << ((sizeof(c) > 1) ? "L'" : "'");
const CharFormat format = PrintAsCharLiteralTo<UnsignedChar>(c, os);
*os << "'";
// To aid user debugging, we also print c's code in decimal, unless
// it's 0 (in which case c was printed as '\\0', making the code
// obvious).
if (c == 0)
return;
*os << " (" << static_cast<int>(c);
// For more convenience, we print c's code again in hexidecimal,
// unless c was already printed in the form '\x##' or the code is in
// [1, 9].
if (format == kHexEscape || (1 <= c && c <= 9)) {
// Do nothing.
} else {
*os << ", 0x" << String::FormatHexInt(static_cast<UnsignedChar>(c));
}
*os << ")";
}
void PrintTo(unsigned char c, ::std::ostream* os) {
PrintCharAndCodeTo<unsigned char>(c, os);
}
void PrintTo(signed char c, ::std::ostream* os) {
PrintCharAndCodeTo<unsigned char>(c, os);
}
// Prints a wchar_t as a symbol if it is printable or as its internal
// code otherwise and also as its code. L'\0' is printed as "L'\\0'".
void PrintTo(wchar_t wc, ostream* os) {
PrintCharAndCodeTo<wchar_t>(wc, os);
}
// Prints the given array of characters to the ostream. CharType must be either
// char or wchar_t.
// The array starts at begin, the length is len, it may include '\0' characters
// and may not be NUL-terminated.
template <typename CharType>
static void PrintCharsAsStringTo(
const CharType* begin, size_t len, ostream* os) {
const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\"";
*os << kQuoteBegin;
bool is_previous_hex = false;
for (size_t index = 0; index < len; ++index) {
const CharType cur = begin[index];
if (is_previous_hex && IsXDigit(cur)) {
// Previous character is of '\x..' form and this character can be
// interpreted as another hexadecimal digit in its number. Break string to
// disambiguate.
*os << "\" " << kQuoteBegin;
}
is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape;
}
*os << "\"";
}
// Prints a (const) char/wchar_t array of 'len' elements, starting at address
// 'begin'. CharType must be either char or wchar_t.
template <typename CharType>
static void UniversalPrintCharArray(
const CharType* begin, size_t len, ostream* os) {
// The code
// const char kFoo[] = "foo";
// generates an array of 4, not 3, elements, with the last one being '\0'.
//
// Therefore when printing a char array, we don't print the last element if
// it's '\0', such that the output matches the string literal as it's
// written in the source code.
if (len > 0 && begin[len - 1] == '\0') {
PrintCharsAsStringTo(begin, len - 1, os);
return;
}
// If, however, the last element in the array is not '\0', e.g.
// const char kFoo[] = { 'f', 'o', 'o' };
// we must print the entire array. We also print a message to indicate
// that the array is not NUL-terminated.
PrintCharsAsStringTo(begin, len, os);
*os << " (no terminating NUL)";
}
// Prints a (const) char array of 'len' elements, starting at address 'begin'.
void UniversalPrintArray(const char* begin, size_t len, ostream* os) {
UniversalPrintCharArray(begin, len, os);
}
// Prints a (const) wchar_t array of 'len' elements, starting at address
// 'begin'.
void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) {
UniversalPrintCharArray(begin, len, os);
}
// Prints the given C string to the ostream.
void PrintTo(const char* s, ostream* os) {
if (s == NULL) {
*os << "NULL";
} else {
*os << ImplicitCast_<const void*>(s) << " pointing to ";
PrintCharsAsStringTo(s, strlen(s), os);
}
}
// MSVC compiler can be configured to define whar_t as a typedef
// of unsigned short. Defining an overload for const wchar_t* in that case
// would cause pointers to unsigned shorts be printed as wide strings,
// possibly accessing more memory than intended and causing invalid
// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when
// wchar_t is implemented as a native type.
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
// Prints the given wide C string to the ostream.
void PrintTo(const wchar_t* s, ostream* os) {
if (s == NULL) {
*os << "NULL";
} else {
*os << ImplicitCast_<const void*>(s) << " pointing to ";
PrintCharsAsStringTo(s, wcslen(s), os);
}
}
#endif // wchar_t is native
// Prints a ::string object.
#if GTEST_HAS_GLOBAL_STRING
void PrintStringTo(const ::string& s, ostream* os) {
PrintCharsAsStringTo(s.data(), s.size(), os);
}
#endif // GTEST_HAS_GLOBAL_STRING
void PrintStringTo(const ::std::string& s, ostream* os) {
PrintCharsAsStringTo(s.data(), s.size(), os);
}
// Prints a ::wstring object.
#if GTEST_HAS_GLOBAL_WSTRING
void PrintWideStringTo(const ::wstring& s, ostream* os) {
PrintCharsAsStringTo(s.data(), s.size(), os);
}
#endif // GTEST_HAS_GLOBAL_WSTRING
#if GTEST_HAS_STD_WSTRING
void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
PrintCharsAsStringTo(s.data(), s.size(), os);
}
#endif // GTEST_HAS_STD_WSTRING
} // namespace internal
} // namespace testing

110
extern/gtest/src/gtest-test-part.cc vendored Normal file
View File

@ -0,0 +1,110 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: mheule@google.com (Markus Heule)
//
// The Google C++ Testing Framework (Google Test)
#include "gtest/gtest-test-part.h"
// Indicates that this translation unit is part of Google Test's
// implementation. It must come before gtest-internal-inl.h is
// included, or there will be a compiler error. This trick is to
// prevent a user from accidentally including gtest-internal-inl.h in
// his code.
#define GTEST_IMPLEMENTATION_ 1
#include "src/gtest-internal-inl.h"
#undef GTEST_IMPLEMENTATION_
namespace testing {
using internal::GetUnitTestImpl;
// Gets the summary of the failure message by omitting the stack trace
// in it.
std::string TestPartResult::ExtractSummary(const char* message) {
const char* const stack_trace = strstr(message, internal::kStackTraceMarker);
return stack_trace == NULL ? message :
std::string(message, stack_trace);
}
// Prints a TestPartResult object.
std::ostream& operator<<(std::ostream& os, const TestPartResult& result) {
return os
<< result.file_name() << ":" << result.line_number() << ": "
<< (result.type() == TestPartResult::kSuccess ? "Success" :
result.type() == TestPartResult::kFatalFailure ? "Fatal failure" :
"Non-fatal failure") << ":\n"
<< result.message() << std::endl;
}
// Appends a TestPartResult to the array.
void TestPartResultArray::Append(const TestPartResult& result) {
array_.push_back(result);
}
// Returns the TestPartResult at the given index (0-based).
const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const {
if (index < 0 || index >= size()) {
printf("\nInvalid index (%d) into TestPartResultArray.\n", index);
internal::posix::Abort();
}
return array_[index];
}
// Returns the number of TestPartResult objects in the array.
int TestPartResultArray::size() const {
return static_cast<int>(array_.size());
}
namespace internal {
HasNewFatalFailureHelper::HasNewFatalFailureHelper()
: has_new_fatal_failure_(false),
original_reporter_(GetUnitTestImpl()->
GetTestPartResultReporterForCurrentThread()) {
GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this);
}
HasNewFatalFailureHelper::~HasNewFatalFailureHelper() {
GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(
original_reporter_);
}
void HasNewFatalFailureHelper::ReportTestPartResult(
const TestPartResult& result) {
if (result.fatally_failed())
has_new_fatal_failure_ = true;
original_reporter_->ReportTestPartResult(result);
}
} // namespace internal
} // namespace testing

110
extern/gtest/src/gtest-typed-test.cc vendored Normal file
View File

@ -0,0 +1,110 @@
// Copyright 2008 Google Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: wan@google.com (Zhanyong Wan)
#include "gtest/gtest-typed-test.h"
#include "gtest/gtest.h"
namespace testing {
namespace internal {
#if GTEST_HAS_TYPED_TEST_P
// Skips to the first non-space char in str. Returns an empty string if str
// contains only whitespace characters.
static const char* SkipSpaces(const char* str) {
while (IsSpace(*str))
str++;
return str;
}
// Verifies that registered_tests match the test names in
// defined_test_names_; returns registered_tests if successful, or
// aborts the program otherwise.
const char* TypedTestCasePState::VerifyRegisteredTestNames(
const char* file, int line, const char* registered_tests) {
typedef ::std::set<const char*>::const_iterator DefinedTestIter;
registered_ = true;
// Skip initial whitespace in registered_tests since some
// preprocessors prefix stringizied literals with whitespace.
registered_tests = SkipSpaces(registered_tests);
Message errors;
::std::set<std::string> tests;
for (const char* names = registered_tests; names != NULL;
names = SkipComma(names)) {
const std::string name = GetPrefixUntilComma(names);
if (tests.count(name) != 0) {
errors << "Test " << name << " is listed more than once.\n";
continue;
}
bool found = false;
for (DefinedTestIter it = defined_test_names_.begin();
it != defined_test_names_.end();
++it) {
if (name == *it) {
found = true;
break;
}
}
if (found) {
tests.insert(name);
} else {
errors << "No test named " << name
<< " can be found in this test case.\n";
}
}
for (DefinedTestIter it = defined_test_names_.begin();
it != defined_test_names_.end();
++it) {
if (tests.count(*it) == 0) {
errors << "You forgot to list test " << *it << ".\n";
}
}
const std::string& errors_str = errors.GetString();
if (errors_str != "") {
fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(),
errors_str.c_str());
fflush(stderr);
posix::Abort();
}
return registered_tests;
}
#endif // GTEST_HAS_TYPED_TEST_P
} // namespace internal
} // namespace testing

5015
extern/gtest/src/gtest.cc vendored Normal file

File diff suppressed because it is too large Load Diff

38
extern/gtest/src/gtest_main.cc vendored Normal file
View File

@ -0,0 +1,38 @@
// Copyright 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h>
#include "gtest/gtest.h"
GTEST_API_ int main(int argc, char **argv) {
printf("Running main() from gtest_main.cc\n");
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -30,6 +30,9 @@ set(INC
.
)
set(INC_SYS
)
set(SRC
libmv-capi.h
libmv-capi_intern.h
@ -67,6 +70,7 @@ if(WITH_LIBMV)
libmv/multiview/fundamental.cc
libmv/multiview/homography.cc
libmv/multiview/panography.cc
libmv/multiview/panography_kernel.cc
libmv/multiview/projection.cc
libmv/multiview/triangulation.cc
libmv/numeric/numeric.cc
@ -92,10 +96,6 @@ if(WITH_LIBMV)
libmv/tracking/track_region.cc
libmv/tracking/trklt_region_tracker.cc
third_party/gflags/gflags.cc
third_party/gflags/gflags_completions.cc
third_party/gflags/gflags_reporting.cc
libmv-util.h
libmv/base/aligned_malloc.h
libmv/base/id_generator.h
@ -106,6 +106,7 @@ if(WITH_LIBMV)
libmv/image/convolve.h
libmv/image/correlation.h
libmv/image/image_converter.h
libmv/image/image_drawing.h
libmv/image/image.h
libmv/image/sample.h
libmv/image/tuple.h
@ -113,13 +114,16 @@ if(WITH_LIBMV)
libmv/multiview/conditioning.h
libmv/multiview/euclidean_resection.h
libmv/multiview/fundamental.h
libmv/multiview/homography_error.h
libmv/multiview/homography.h
libmv/multiview/homography_parameterization.h
libmv/multiview/nviewtriangulation.h
libmv/multiview/panography.h
libmv/multiview/panography_kernel.h
libmv/multiview/projection.h
libmv/multiview/resection.h
libmv/multiview/triangulation.h
libmv/multiview/two_view_kernel.h
libmv/numeric/dogleg.h
libmv/numeric/function_derivative.h
libmv/numeric/levenberg_marquardt.h
@ -149,18 +153,86 @@ if(WITH_LIBMV)
libmv/tracking/track_region.h
libmv/tracking/trklt_region_tracker.h
third_party/msinttypes/inttypes.h
third_party/msinttypes/stdint.h
)
if(WIN32)
list(APPEND INC
third_party/glog/src/windows
)
if(NOT MINGW)
list(APPEND INC
third_party/msinttypes
)
endif()
endif()
if(WITH_TESTS)
blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "" "")
endif()
BLENDER_SRC_GTEST("libmv_scoped_ptr" "./libmv/base/scoped_ptr_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_vector" "./libmv/base/vector_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_array_nd" "./libmv/image/array_nd_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_convolve" "./libmv/image/convolve_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_image" "./libmv/image/image_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_sample" "./libmv/image/sample_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_tuple" "./libmv/image/tuple_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_euclidean_resection" "./libmv/multiview/euclidean_resection_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_fundamental" "./libmv/multiview/fundamental_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_homography" "./libmv/multiview/homography_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_nviewtriangulation" "./libmv/multiview/nviewtriangulation_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_panography" "./libmv/multiview/panography_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_projection" "./libmv/multiview/projection_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_resection" "./libmv/multiview/resection_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_triangulation" "./libmv/multiview/triangulation_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_dogleg" "./libmv/numeric/dogleg_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_function_derivative" "./libmv/numeric/function_derivative_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_levenberg_marquardt" "./libmv/numeric/levenberg_marquardt_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_numeric" "./libmv/numeric/numeric_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_poly" "./libmv/numeric/poly_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_camera_intrinsics" "./libmv/simple_pipeline/camera_intrinsics_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_detect" "./libmv/simple_pipeline/detect_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_intersect" "./libmv/simple_pipeline/intersect_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_keyframe_selection" "./libmv/simple_pipeline/keyframe_selection_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_modal_solver" "./libmv/simple_pipeline/modal_solver_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_resect" "./libmv/simple_pipeline/resect_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_brute_region_tracker" "./libmv/tracking/brute_region_tracker_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_klt_region_tracker" "./libmv/tracking/klt_region_tracker_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
BLENDER_SRC_GTEST("libmv_pyramid_region_tracker" "./libmv/tracking/pyramid_region_tracker_test.cc" "libmv_test_dataset;extern_libmv;extern_ceres")
else()
list(APPEND SRC
libmv-capi_stub.cc
)
endif()
blender_add_lib(extern_libmv "${SRC}" "${INC}" "${INC_SYS}")
if(WITH_LIBMV)
add_subdirectory(third_party)
endif()
# make GLog a separate target, so it can be used for gtest as well.
if(WITH_LIBMV OR WITH_TESTS)
# We compile GLog together with GFlag so we don't worry about
# adding extra lib to linker.
set(GLOG_SRC
third_party/gflags/gflags.cc
third_party/gflags/gflags_completions.cc
third_party/gflags/gflags_reporting.cc
third_party/gflags/config.h
third_party/gflags/gflags/gflags_completions.h
third_party/gflags/gflags/gflags_declare.h
third_party/gflags/gflags/gflags.h
third_party/gflags/mutex.h
third_party/gflags/util.h
third_party/msinttypes/inttypes.h
third_party/msinttypes/stdint.h
)
if(WIN32)
list(APPEND SRC
list(APPEND GLOG_SRC
third_party/glog/src/logging.cc
third_party/glog/src/raw_logging.cc
third_party/glog/src/utilities.cc
@ -185,18 +257,8 @@ if(WITH_LIBMV)
third_party/glog/src/windows/port.h
third_party/glog/src/windows/config.h
)
list(APPEND INC
third_party/glog/src/windows
)
if(NOT MINGW)
list(APPEND INC
third_party/msinttypes
)
endif()
else()
list(APPEND SRC
list(APPEND GLOG_SRC
third_party/glog/src/demangle.cc
third_party/glog/src/logging.cc
third_party/glog/src/raw_logging.cc
@ -228,14 +290,14 @@ if(WITH_LIBMV)
third_party/glog/src/utilities.h
)
endif()
else()
list(APPEND SRC
libmv-capi_stub.cc
set(GLOG_INC
third_party/gflags
third_party/glog/src
)
endif()
blender_add_lib(extern_libmv "${SRC}" "${INC}" "${INC_SYS}")
set(GLOG_INC_SYS
)
if(WITH_LIBMV)
add_subdirectory(third_party)
blender_add_lib(extern_glog "${GLOG_SRC}" "${GLOG_INC}" "${GLOG_INC_SYS}")
endif()

View File

@ -44,6 +44,8 @@ if env['WITH_BF_LIBMV']:
else:
src = env.Glob("libmv-capi_stub.cc")
src = [src for src in src if src.find('_test.cc') == -1]
env.BlenderLib ( libname = 'extern_libmv', sources=src, includes=Split(incs), defines=defs, libtype=['extern', 'player'], priority=[20,137] )
if env['WITH_BF_LIBMV']:

View File

@ -30,15 +30,20 @@ rm -rf $tmp
chmod 664 ./third_party/glog/src/windows/*.cc ./third_party/glog/src/windows/*.h ./third_party/glog/src/windows/glog/*.h
sources=`find ./libmv -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\//\t\t/' | sort -d`
headers=`find ./libmv -type f -iname '*.h' | sed -r 's/^\.\//\t\t/' | sort -d`
sources=`find ./libmv -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v _test.cc | grep -v test_data_sets | sed -r 's/^\.\//\t\t/' | sort -d`
headers=`find ./libmv -type f -iname '*.h' | grep -v test_data_sets | sed -r 's/^\.\//\t\t/' | sort -d`
third_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v glog | grep -v ceres | sed -r 's/^\.\//\t\t/' | sort -d`
third_headers=`find ./third_party -type f -iname '*.h' | grep -v glog | grep -v ceres | sed -r 's/^\.\//\t\t/' | sort -d`
third_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v glog | grep -v gflags | grep -v ceres | sed -r 's/^\.\//\t\t/' | sort -d`
third_headers=`find ./third_party -type f -iname '*.h' | grep -v glog | grep -v gflags | grep -v ceres | sed -r 's/^\.\//\t\t/' | sort -d`
third_glog_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep glog | grep -v windows | sed -r 's/^\.\//\t\t\t/' | sort -d`
third_glog_headers=`find ./third_party -type f -iname '*.h' | grep glog | grep -v windows | sed -r 's/^\.\//\t\t\t/' | sort -d`
third_gflags_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep gflags | grep -v windows | sed -r 's/^\.\//\t\t/' | sort -d`
third_gflags_headers=`find ./third_party -type f -iname '*.h' | grep gflags | grep -v windows | sed -r 's/^\.\//\t\t/' | sort -d`
tests=`find ./libmv -type f -iname '*_test.cc' | sort -d | awk ' { name=gensub(".*/([A-Za-z_]+)_test.cc", "\\\\1", $1); printf("\tBLENDER_SRC_GTEST(\"libmv_%s\" \"%s\" \"libmv_test_dataset;extern_libmv;extern_ceres\")\n", name, $1) } '`
src_dir=`find ./libmv -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\//\t\t/' | sort -d | uniq`
src_third_dir=`find ./third_party -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | grep -v ceres | sed -r 's/^\.\//\t\t/' | sort -d | uniq`
src=""
@ -118,6 +123,9 @@ set(INC
.
)
set(INC_SYS
)
set(SRC
libmv-capi.h
libmv-capi_intern.h
@ -148,9 +156,7 @@ if(WITH_LIBMV)
libmv-capi.cc
libmv-util.cc
${sources}
${third_sources}
libmv-util.h
${headers}
@ -158,7 +164,46 @@ ${third_headers}
)
if(WIN32)
list(APPEND SRC
list(APPEND INC
third_party/glog/src/windows
)
if(NOT MINGW)
list(APPEND INC
third_party/msinttypes
)
endif()
endif()
if(WITH_TESTS)
blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "${INC}" "${INC_SYS}")
endif()
${tests}
else()
list(APPEND SRC
libmv-capi_stub.cc
)
endif()
blender_add_lib(extern_libmv "\${SRC}" "\${INC}" "\${INC_SYS}")
if(WITH_LIBMV)
add_subdirectory(third_party)
endif()
# make GLog a separate target, so it can be used for gtest as well.
if(WITH_LIBMV OR WITH_TESTS)
# We compile GLog together with GFlag so we don't worry about
# adding extra lib to linker.
set(GLOG_SRC
${third_gflags_sources}
${third_gflags_headers}
)
if(WIN32)
list(APPEND GLOG_SRC
third_party/glog/src/logging.cc
third_party/glog/src/raw_logging.cc
third_party/glog/src/utilities.cc
@ -183,33 +228,23 @@ ${third_headers}
third_party/glog/src/windows/port.h
third_party/glog/src/windows/config.h
)
list(APPEND INC
third_party/glog/src/windows
)
if(NOT MINGW)
list(APPEND INC
third_party/msinttypes
)
endif()
else()
list(APPEND SRC
list(APPEND GLOG_SRC
${third_glog_sources}
${third_glog_headers}
)
endif()
else()
list(APPEND SRC
libmv-capi_stub.cc
set(GLOG_INC
third_party/gflags
third_party/glog/src
)
endif()
blender_add_lib(extern_libmv "\${SRC}" "\${INC}" "\${INC_SYS}")
set(GLOG_INC_SYS
)
if(WITH_LIBMV)
add_subdirectory(third_party)
blender_add_lib(extern_glog "\${GLOG_SRC}" "\${GLOG_INC}" "\${GLOG_INC_SYS}")
endif()
EOF
@ -254,6 +289,8 @@ ${win_src}
else:
src = env.Glob("libmv-capi_stub.cc")
src = [src for src in src if src.find('_test.cc') == -1]
env.BlenderLib ( libname = 'extern_libmv', sources=src, includes=Split(incs), defines=defs, libtype=['extern', 'player'], priority=[20,137] )
if env['WITH_BF_LIBMV']:

View File

@ -2,60 +2,92 @@ libmv/base/aligned_malloc.cc
libmv/base/aligned_malloc.h
libmv/base/id_generator.h
libmv/base/scoped_ptr.h
libmv/base/scoped_ptr_test.cc
libmv/base/vector.h
libmv/base/vector_test.cc
libmv/base/vector_utils.h
libmv/image/array_nd.cc
libmv/image/array_nd.h
libmv/image/array_nd_test.cc
libmv/image/convolve.cc
libmv/image/convolve.h
libmv/image/convolve_test.cc
libmv/image/correlation.h
libmv/image/image_converter.h
libmv/image/image_drawing.h
libmv/image/image.h
libmv/image/image_test.cc
libmv/image/sample.h
libmv/image/sample_test.cc
libmv/image/tuple.h
libmv/image/tuple_test.cc
libmv/logging/logging.h
libmv/multiview/conditioning.cc
libmv/multiview/conditioning.h
libmv/multiview/euclidean_resection.cc
libmv/multiview/euclidean_resection.h
libmv/multiview/euclidean_resection_test.cc
libmv/multiview/fundamental.cc
libmv/multiview/fundamental.h
libmv/multiview/fundamental_test.cc
libmv/multiview/homography.cc
libmv/multiview/homography_error.h
libmv/multiview/homography.h
libmv/multiview/homography_parameterization.h
libmv/multiview/homography_test.cc
libmv/multiview/nviewtriangulation.h
libmv/multiview/nviewtriangulation_test.cc
libmv/multiview/panography.cc
libmv/multiview/panography.h
libmv/multiview/panography_kernel.cc
libmv/multiview/panography_kernel.h
libmv/multiview/panography_test.cc
libmv/multiview/projection.cc
libmv/multiview/projection.h
libmv/multiview/projection_test.cc
libmv/multiview/resection.h
libmv/multiview/resection_test.cc
libmv/multiview/test_data_sets.cc
libmv/multiview/test_data_sets.h
libmv/multiview/triangulation.cc
libmv/multiview/triangulation.h
libmv/multiview/triangulation_test.cc
libmv/multiview/two_view_kernel.h
libmv/numeric/dogleg.h
libmv/numeric/dogleg_test.cc
libmv/numeric/function_derivative.h
libmv/numeric/function_derivative_test.cc
libmv/numeric/levenberg_marquardt.h
libmv/numeric/levenberg_marquardt_test.cc
libmv/numeric/numeric.cc
libmv/numeric/numeric.h
libmv/numeric/numeric_test.cc
libmv/numeric/poly.cc
libmv/numeric/poly.h
libmv/numeric/poly_test.cc
libmv/simple_pipeline/bundle.cc
libmv/simple_pipeline/bundle.h
libmv/simple_pipeline/callbacks.h
libmv/simple_pipeline/camera_intrinsics.cc
libmv/simple_pipeline/camera_intrinsics.h
libmv/simple_pipeline/camera_intrinsics_impl.h
libmv/simple_pipeline/camera_intrinsics_test.cc
libmv/simple_pipeline/detect.cc
libmv/simple_pipeline/detect.h
libmv/simple_pipeline/detect_test.cc
libmv/simple_pipeline/distortion_models.cc
libmv/simple_pipeline/distortion_models.h
libmv/simple_pipeline/initialize_reconstruction.cc
libmv/simple_pipeline/initialize_reconstruction.h
libmv/simple_pipeline/intersect.cc
libmv/simple_pipeline/intersect.h
libmv/simple_pipeline/intersect_test.cc
libmv/simple_pipeline/keyframe_selection.cc
libmv/simple_pipeline/keyframe_selection.h
libmv/simple_pipeline/keyframe_selection_test.cc
libmv/simple_pipeline/modal_solver.cc
libmv/simple_pipeline/modal_solver.h
libmv/simple_pipeline/modal_solver_test.cc
libmv/simple_pipeline/pipeline.cc
libmv/simple_pipeline/pipeline.h
libmv/simple_pipeline/reconstruction.cc
@ -64,16 +96,20 @@ libmv/simple_pipeline/reconstruction_scale.cc
libmv/simple_pipeline/reconstruction_scale.h
libmv/simple_pipeline/resect.cc
libmv/simple_pipeline/resect.h
libmv/simple_pipeline/resect_test.cc
libmv/simple_pipeline/tracks.cc
libmv/simple_pipeline/tracks.h
libmv/tracking/brute_region_tracker.cc
libmv/tracking/brute_region_tracker.h
libmv/tracking/brute_region_tracker_test.cc
libmv/tracking/hybrid_region_tracker.cc
libmv/tracking/hybrid_region_tracker.h
libmv/tracking/klt_region_tracker.cc
libmv/tracking/klt_region_tracker.h
libmv/tracking/klt_region_tracker_test.cc
libmv/tracking/pyramid_region_tracker.cc
libmv/tracking/pyramid_region_tracker.h
libmv/tracking/pyramid_region_tracker_test.cc
libmv/tracking/region_tracker.h
libmv/tracking/retrack_region_tracker.cc
libmv/tracking/retrack_region_tracker.h

View File

@ -0,0 +1,79 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/base/scoped_ptr.h"
#include "testing/testing.h"
namespace libmv {
namespace {
struct FreeMe {
FreeMe(int *freed) : freed(freed) {}
~FreeMe() { (*freed)++; }
int *freed;
};
TEST(ScopedPtr, NullDoesNothing) {
scoped_ptr<FreeMe> x(NULL);
// Does nothing.
}
TEST(ScopedPtr, FreesWhenOutOfScope) {
int frees = 0;
{
scoped_ptr<FreeMe> scoped(new FreeMe(&frees));
EXPECT_EQ(0, frees);
}
EXPECT_EQ(1, frees);
}
TEST(ScopedPtr, Operators) {
int tag = 123;
scoped_ptr<FreeMe> scoped(new FreeMe(&tag));
EXPECT_EQ(123, *(scoped->freed));
EXPECT_EQ(123, *((*scoped).freed));
}
TEST(ScopedPtr, Reset) {
int frees = 0;
scoped_ptr<FreeMe> scoped(new FreeMe(&frees));
EXPECT_EQ(0, frees);
scoped.reset(new FreeMe(&frees));
EXPECT_EQ(1, frees);
}
TEST(ScopedPtr, ReleaseAndGet) {
int frees = 0;
FreeMe *allocated = new FreeMe(&frees);
FreeMe *released = NULL;
{
scoped_ptr<FreeMe> scoped(allocated);
EXPECT_EQ(0, frees);
EXPECT_EQ(allocated, scoped.get());
released = scoped.release();
EXPECT_EQ(0, frees);
EXPECT_EQ(released, allocated);
}
EXPECT_EQ(0, frees);
delete released;
}
} // namespace
} // namespace libmv

223
extern/libmv/libmv/base/vector_test.cc vendored Normal file
View File

@ -0,0 +1,223 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/base/vector.h"
#include "libmv/numeric/numeric.h"
#include "testing/testing.h"
#include <algorithm>
namespace {
using namespace libmv;
// This uses a Vec2d which is a fixed-size vectorizable Eigen type. It is
// necessary to test vectorizable types to ensure that the alignment asserts
// trigger if the alignment is not correct.
TEST(VectorAlignmentTest, PushBack) {
Vec2 x1, x2;
x1 << 1, 2;
x2 << 3, 4;
vector<Vec2> vs;
vs.push_back(x1);
EXPECT_EQ(1, vs.size());
EXPECT_EQ(1, vs.capacity());
vs.push_back(x2);
EXPECT_EQ(2, vs.size());
EXPECT_EQ(2, vs.capacity());
// The following is necessary because of some bug in gtest; the expected
// parameter can't be a fixed size vectorizable type with alignment
// requirements.
Vec x1r = x1;
Vec x2r = x2;
EXPECT_EQ(x1r, vs[0]);
EXPECT_EQ(x2r, vs[1]);
vs.push_back(x2);
vs.push_back(x2);
vs.push_back(x2);
EXPECT_EQ(5, vs.size());
EXPECT_EQ(8, vs.capacity());
}
// Count the number of destruct calls to test that the destructor gets called.
int foo_construct_calls = 0;
int foo_destruct_calls = 0;
struct Foo {
public:
Foo() : value(5) { foo_construct_calls++; }
~Foo() { foo_destruct_calls++; }
int value;
};
struct VectorTest : public testing::Test {
VectorTest() {
foo_construct_calls = 0;
foo_destruct_calls = 0;
}
};
TEST_F(VectorTest, EmptyVectorDoesNotConstruct) {
{
vector<Foo> v;
EXPECT_EQ(0, v.size());
EXPECT_EQ(0, v.capacity());
}
EXPECT_EQ(0, foo_construct_calls);
EXPECT_EQ(0, foo_destruct_calls);
}
TEST_F(VectorTest, DestructorGetsCalled) {
{
vector<Foo> v;
v.resize(5);
}
EXPECT_EQ(5, foo_construct_calls);
EXPECT_EQ(5, foo_destruct_calls);
}
TEST_F(VectorTest, ReserveDoesNotCallConstructorsOrDestructors) {
vector<Foo> v;
EXPECT_EQ(0, v.size());
EXPECT_EQ(0, v.capacity());
EXPECT_EQ(0, foo_construct_calls);
EXPECT_EQ(0, foo_destruct_calls);
v.reserve(5);
EXPECT_EQ(0, v.size());
EXPECT_EQ(5, v.capacity());
EXPECT_EQ(0, foo_construct_calls);
EXPECT_EQ(0, foo_destruct_calls);
}
TEST_F(VectorTest, ResizeConstructsAndDestructsAsExpected) {
vector<Foo> v;
// Create one object.
v.resize(1);
EXPECT_EQ(1, v.size());
EXPECT_EQ(1, v.capacity());
EXPECT_EQ(1, foo_construct_calls);
EXPECT_EQ(0, foo_destruct_calls);
EXPECT_EQ(5, v[0].value);
// Create two more.
v.resize(3);
EXPECT_EQ(3, v.size());
EXPECT_EQ(3, v.capacity());
EXPECT_EQ(3, foo_construct_calls);
EXPECT_EQ(0, foo_destruct_calls);
// Delete the last one.
v.resize(2);
EXPECT_EQ(2, v.size());
EXPECT_EQ(3, v.capacity());
EXPECT_EQ(3, foo_construct_calls);
EXPECT_EQ(1, foo_destruct_calls);
// Delete the remaining two.
v.resize(0);
EXPECT_EQ(0, v.size());
EXPECT_EQ(3, v.capacity());
EXPECT_EQ(3, foo_construct_calls);
EXPECT_EQ(3, foo_destruct_calls);
}
TEST_F(VectorTest, PushPopBack) {
vector<Foo> v;
Foo foo;
foo.value = 10;
v.push_back(foo);
EXPECT_EQ(1, v.size());
EXPECT_EQ(10, v.back().value);
v.pop_back();
EXPECT_EQ(0, v.size());
EXPECT_EQ(1, foo_construct_calls);
EXPECT_EQ(1, foo_destruct_calls);
}
TEST_F(VectorTest, CopyConstructor) {
vector<int> a;
a.push_back(1);
a.push_back(5);
a.push_back(3);
vector<int> b(a);
EXPECT_EQ(a.size(), b.size());
//EXPECT_EQ(a.capacity(), b.capacity());
for (int i = 0; i < a.size(); ++i) {
EXPECT_EQ(a[i], b[i]);
}
}
TEST_F(VectorTest, OperatorEquals) {
vector<int> a, b;
a.push_back(1);
a.push_back(5);
a.push_back(3);
b = a;
EXPECT_EQ(a.size(), b.size());
//EXPECT_EQ(a.capacity(), b.capacity());
for (int i = 0; i < a.size(); ++i) {
EXPECT_EQ(a[i], b[i]);
}
}
TEST_F(VectorTest, STLFind) {
vector<int> a;
a.push_back(1);
a.push_back(5);
a.push_back(3);
// Find return an int *
EXPECT_EQ(std::find(&a[0], &a[2], 1) == &a[0], true);
EXPECT_EQ(std::find(&a[0], &a[2], 5) == &a[1], true);
EXPECT_EQ(std::find(&a[0], &a[2], 3) == &a[2], true);
// Find return a const int *
EXPECT_EQ(std::find(a.begin(), a.end(), 1) == &a[0], true);
EXPECT_EQ(std::find(a.begin(), a.end(), 5) == &a[1], true);
EXPECT_EQ(std::find(a.begin(), a.end(), 3) == &a[2], true);
// Search value that are not in the vector
EXPECT_EQ(std::find(a.begin(), a.end(), 0) == a.end(), true);
EXPECT_EQ(std::find(a.begin(), a.end(), 52) == a.end(), true);
}
TEST(Vector, swap) {
vector<int> a, b;
a.push_back(1);
a.push_back(2);
b.push_back(3);
a.swap(b);
EXPECT_EQ(1, a.size());
EXPECT_EQ(3, a[0]);
EXPECT_EQ(2, b.size());
EXPECT_EQ(1, b[0]);
EXPECT_EQ(2, b[1]);
}
} // namespace

View File

@ -0,0 +1,324 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/image/array_nd.h"
#include "testing/testing.h"
using libmv::ArrayND;
using libmv::Array3D;
using libmv::Array3Df;
namespace {
TEST(ArrayND, EmptyConstructor) {
ArrayND<int, 2> a;
EXPECT_EQ(0, a.Shape(0));
EXPECT_EQ(0, a.Shape(1));
}
TEST(ArrayND, IndexConstructor) {
int s[] = {1, 2, 3};
ArrayND<int, 3>::Index shape(s);
ArrayND<int, 3> a(shape);
EXPECT_EQ(1, a.Shape(0));
EXPECT_EQ(2, a.Shape(1));
EXPECT_EQ(3, a.Shape(2));
}
TEST(ArrayND, PointerConstructor) {
int s[] = {1, 2, 3};
ArrayND<int, 3> a(s);
EXPECT_EQ(1, a.Shape(0));
EXPECT_EQ(2, a.Shape(1));
EXPECT_EQ(3, a.Shape(2));
}
TEST(ArrayND, CopyConstructor) {
int s[] = {1, 2, 3};
ArrayND<int, 3> a(s);
a(0, 1, 1) = 3;
a(0, 1, 2) = 3;
ArrayND<int, 3> b(a);
EXPECT_EQ(1, b.Shape(0));
EXPECT_EQ(2, b.Shape(1));
EXPECT_EQ(3, b.Shape(2));
EXPECT_EQ(3, b(0, 1, 1));
b(0, 1, 2) = 2;
EXPECT_EQ(3, a(0, 1, 2));
}
TEST(ArrayND, Assignation) {
int s[] = {1, 2, 3};
ArrayND<int, 3> a(s);
a(0, 1, 1) = 3;
a(0, 1, 2) = 3;
ArrayND<int, 3> b;
b = a;
EXPECT_EQ(1, b.Shape(0));
EXPECT_EQ(2, b.Shape(1));
EXPECT_EQ(3, b.Shape(2));
EXPECT_EQ(3, b(0, 1, 1));
b(0, 1, 2) = 2;
EXPECT_EQ(3, a(0, 1, 2));
}
TEST(ArrayND, Fill) {
int s[] = {2, 2};
ArrayND<int, 2> a(s);
a.Fill(42);
EXPECT_EQ(42, a(0, 0));
EXPECT_EQ(42, a(0, 1));
EXPECT_EQ(42, a(1, 0));
EXPECT_EQ(42, a(1, 1));
}
TEST(ArrayND, Size) {
int s[] = {1, 2, 3};
ArrayND<int, 3>::Index shape(s);
ArrayND<int, 3> a(shape);
int l[] = {0, 1, 2};
ArrayND<int, 3>::Index last(l);
EXPECT_EQ(a.Size(), a.Offset(last)+1);
EXPECT_TRUE(a.Contains(last));
EXPECT_FALSE(a.Contains(shape));
}
TEST(ArrayND, MemorySizeInBytes) {
int s[] = {2, 3};
ArrayND<int, 2>::Index shape(s);
ArrayND<int, 2> a(shape);
int size = 24 + sizeof(a);
EXPECT_EQ(size, a.MemorySizeInBytes());
}
TEST(ArrayND, Parenthesis) {
typedef ArrayND<int, 2>::Index Index;
int s[] = {3, 3};
ArrayND<int, 2> a(s);
*(a.Data()+0) = 0;
*(a.Data()+5) = 5;
int i1[] = {0, 0};
EXPECT_EQ(0, a(Index(i1)));
int i2[] = {1, 2};
EXPECT_EQ(5, a(Index(i2)));
}
TEST(ArrayND, 1DConstructor) {
ArrayND<int, 1> a(3);
EXPECT_EQ(3, a.Shape(0));
}
TEST(ArrayND, 2DConstructor) {
ArrayND<int, 2> a(1, 2);
EXPECT_EQ(1, a.Shape(0));
EXPECT_EQ(2, a.Shape(1));
}
TEST(ArrayND, 3DConstructor) {
ArrayND<int, 3> a(1, 2, 3);
EXPECT_EQ(1, a.Shape(0));
EXPECT_EQ(2, a.Shape(1));
EXPECT_EQ(3, a.Shape(2));
}
TEST(ArrayND, 1DAccessor) {
ArrayND<int, 1> a(3);
a(0) = 1;
a(1) = 2;
EXPECT_EQ(1, a(0));
EXPECT_EQ(2, a(1));
EXPECT_EQ(1, *(a.Data()));
EXPECT_EQ(2, *(a.Data() + a.Stride(0)));
}
TEST(ArrayND, 2DAccessor) {
ArrayND<int, 2> a(3, 3);
a(0, 0) = 1;
a(1, 1) = 2;
EXPECT_EQ(1, a(0, 0));
EXPECT_EQ(2, a(1, 1));
EXPECT_EQ(1, *(a.Data()));
EXPECT_EQ(2, *(a.Data() + a.Stride(0) + a.Stride(1)));
}
TEST(ArrayND, 3DAccessor) {
ArrayND<int, 3> a(3, 3, 3);
a(0, 0, 0) = 1;
a(1, 1, 1) = 2;
EXPECT_EQ(1, a(0, 0, 0));
EXPECT_EQ(2, a(1, 1, 1));
EXPECT_EQ(1, *(a.Data()));
EXPECT_EQ(2, *(a.Data() + a.Stride(0) + a.Stride(1) + a.Stride(2)));
}
TEST(ArrayND, CopyFrom) {
ArrayND<int, 3> a(2, 2, 1);
a(0, 0, 0) = 1;
a(0, 1, 0) = 2;
a(1, 0, 0) = 3;
a(1, 1, 0) = 4;
ArrayND<float, 3> b;
b.CopyFrom(a);
EXPECT_FLOAT_EQ(1.f, b(0, 0, 0));
EXPECT_FLOAT_EQ(2.f, b(0, 1, 0));
EXPECT_FLOAT_EQ(3.f, b(1, 0, 0));
EXPECT_FLOAT_EQ(4.f, b(1, 1, 0));
}
TEST(ArrayND, MultiplyElements) {
ArrayND<int, 3> a(2, 2, 1);
a(0, 0, 0) = 1;
a(0, 1, 0) = 2;
a(1, 0, 0) = 3;
a(1, 1, 0) = 4;
ArrayND<int, 3> b(2, 2, 1);
b(0, 0, 0) = 6;
b(0, 1, 0) = 5;
b(1, 0, 0) = 4;
b(1, 1, 0) = 3;
ArrayND<int, 3> c;
MultiplyElements(a, b, &c);
EXPECT_FLOAT_EQ(6, c(0, 0, 0));
EXPECT_FLOAT_EQ(10, c(0, 1, 0));
EXPECT_FLOAT_EQ(12, c(1, 0, 0));
EXPECT_FLOAT_EQ(12, c(1, 1, 0));
}
TEST(ArrayND, IsEqualOperator) {
ArrayND<int, 3> a(2, 2, 1);
a(0, 0, 0) = 1;
a(0, 1, 0) = 2;
a(1, 0, 0) = 3;
a(1, 1, 0) = 4;
ArrayND<int, 3> b(2, 2, 1);
b(0, 0, 0) = 1;
b(0, 1, 0) = 2;
b(1, 0, 0) = 3;
b(1, 1, 0) = 4;
EXPECT_TRUE(a == b);
EXPECT_FALSE(a != b);
b(1, 1, 0) = 5;
EXPECT_TRUE(a != b);
EXPECT_FALSE(a == b);
}
TEST(Array3D, Sizes) {
Array3D<int> array;
EXPECT_EQ(array.Height(), 0);
EXPECT_EQ(array.Width(), 0);
EXPECT_EQ(array.Depth(), 0);
EXPECT_EQ(array.Shape(0), 0);
}
TEST(Array3D, CopyConstructor) {
Array3D<int> array(10, 10);
array(0, 0) = 1;
array(0, 1) = 1;
Array3D<int> copy(array);
EXPECT_EQ(copy.Height(), 10);
EXPECT_EQ(copy.Width(), 10);
EXPECT_EQ(copy.Depth(), 1);
EXPECT_EQ(copy(0, 0), 1);
copy(0, 1) = 2;
EXPECT_EQ(array(0, 1), 1);
}
TEST(Array3D, Assignation) {
Array3D<int> array(10, 10);
array(0, 0) = 1;
array(0, 1) = 1;
Array3D<int> copy;
copy = array;
EXPECT_EQ(copy.Height(), 10);
EXPECT_EQ(copy.Width(), 10);
EXPECT_EQ(copy.Depth(), 1);
EXPECT_EQ(copy(0, 0), 1);
copy(0, 1) = 2;
EXPECT_EQ(array(0, 1), 1);
}
TEST(Array3D, Parenthesis) {
Array3D<int> array(1, 2, 3);
array(0, 1, 0) = 3;
EXPECT_EQ(array(0, 1), 3);
}
TEST(Array3Df, SplitChannels) {
Array3Df array(1, 2, 3);
array(0, 0, 0) = 1;
array(0, 1, 0) = 1;
array(0, 0, 1) = 2;
array(0, 1, 1) = 2;
array(0, 0, 2) = 3;
array(0, 1, 2) = 3;
Array3Df c0, c1, c2;
SplitChannels(array, &c0, &c1, &c2);
for (int row = 0; row < 1; ++row) {
for (int column = 0; column < 2; ++column) {
EXPECT_EQ(array(row, column, 0), c0(row, column));
EXPECT_EQ(array(row, column, 1), c1(row, column));
EXPECT_EQ(array(row, column, 2), c2(row, column));
}
}
}
TEST(ArrayND, MultiplyElementsGeneric) {
ArrayND<double, 5> A;
ArrayND<int, 5> B;
ArrayND<double, 5> C;
int shape[] = {1, 3, 5, 7, 1};
A.Resize(shape);
B.Resize(shape);
A.Fill(1.1);
B.Fill(2);
MultiplyElements(A, B, &C);
ArrayND<double, 5>::Index cIndex;
for (int d0 = 0; d0 < shape[0]; ++d0)
for (int d1 = 0; d1 < shape[1]; ++d1)
for (int d2 = 0; d2 < shape[2]; ++d2)
for (int d3 = 0; d3 < shape[3]; ++d3)
for (int d4 = 0; d4 < shape[4]; ++d4) {
cIndex(0) = d0;
cIndex(1) = d1;
cIndex(2) = d2;
cIndex(3) = d3;
cIndex(4) = d4;
EXPECT_EQ(2.2, C(cIndex));
}
}
} // namespace

View File

@ -0,0 +1,110 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include <iostream>
#include "libmv/image/convolve.h"
#include "libmv/image/image.h"
#include "libmv/numeric/numeric.h"
#include "testing/testing.h"
using namespace libmv;
namespace {
TEST(Convolve, ComputeGaussianKernel) {
Vec kernel, derivative;
ComputeGaussianKernel(1, &kernel, &derivative);
EXPECT_EQ(7, kernel.size());
// TODO(keir): Put in a more thorough test here!
}
TEST(Convolve, ConvolveGaussian) {
FloatImage im(10, 10);
im.Fill(1);
FloatImage blured;
ConvolveGaussian(im, 3, &blured);
EXPECT_NEAR(im(5, 5), 1, 1e-7);
}
TEST(Convolve, BoxFilterHorizontal) {
FloatImage im(10, 10), convolved, filtered;
im.Fill(1);
BoxFilterHorizontal(im, 3, &filtered);
Vec kernel(3);
kernel.setConstant(1.);
ConvolveHorizontal(im, kernel, &convolved);
EXPECT_EQ(filtered(5, 5), 3);
EXPECT_TRUE(filtered == convolved);
}
TEST(Convolve, BoxFilter) {
FloatImage image(5, 5), filtered;
// A single 1.0 inside a 5x5 image should expand to a 3x3 square.
image.Fill(0);
image(2, 2) = 1.0;
BoxFilter(image, 3, &filtered);
for (int j = 0; j < 5; j++) {
for (int i = 0; i < 5; i++) {
if (i == 0 || i == 4 || j == 0 || j == 4) {
EXPECT_EQ(0.0, filtered(j, i));
} else {
EXPECT_EQ(1.0, filtered(j, i));
}
}
}
}
TEST(Convolve, BlurredImageAndDerivativesChannelsFlat) {
FloatImage im(10, 10), blurred_and_derivatives;
im.Fill(1);
BlurredImageAndDerivativesChannels(im, 1.0, &blurred_and_derivatives);
EXPECT_NEAR(blurred_and_derivatives(5, 5, 0), 1.0, 1e-7);
EXPECT_NEAR(blurred_and_derivatives(5, 5, 1), 0.0, 1e-7);
EXPECT_NEAR(blurred_and_derivatives(5, 5, 2), 0.0, 1e-7);
}
TEST(Convolve, BlurredImageAndDerivativesChannelsHorizontalSlope) {
FloatImage image(10, 10), blurred_and_derivatives;
for (int j = 0; j < 10; ++j) {
for (int i = 0; i < 10; ++i) {
image(j, i) = 2*i;
}
}
BlurredImageAndDerivativesChannels(image, 0.9, &blurred_and_derivatives);
EXPECT_NEAR(blurred_and_derivatives(5, 5, 0), 10.0, 1e-7);
EXPECT_NEAR(blurred_and_derivatives(5, 5, 1), 2.0, 1e-7);
EXPECT_NEAR(blurred_and_derivatives(5, 5, 2), 0.0, 1e-7);
}
TEST(Convolve, BlurredImageAndDerivativesChannelsVerticalSlope) {
FloatImage image(10, 10), blurred_and_derivatives;
for (int j = 0; j < 10; ++j) {
for (int i = 0; i < 10; ++i) {
image(j, i) = 2*j;
}
}
BlurredImageAndDerivativesChannels(image, 0.9, &blurred_and_derivatives);
EXPECT_NEAR(blurred_and_derivatives(5, 5, 0), 10.0, 1e-7);
EXPECT_NEAR(blurred_and_derivatives(5, 5, 1), 0.0, 1e-7);
EXPECT_NEAR(blurred_and_derivatives(5, 5, 2), 2.0, 1e-7);
}
} // namespace

285
extern/libmv/libmv/image/image_drawing.h vendored Normal file
View File

@ -0,0 +1,285 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// Generic Image Processing Algorithm (GIPA)
// Use an ImageModel class that must implement the following :
//
// ::Contains(int y, int x) <= Tell if a point is inside or not the image
// ::operator(int y,int x) <= Modification accessor over the pixel (y,x)
// ::Width()
// ::Height()
#ifndef LIBMV_IMAGE_IMAGE_DRAWING_H
#define LIBMV_IMAGE_IMAGE_DRAWING_H
namespace libmv {
/// Put the pixel in the image to the given color only if the point (xc,yc)
/// is inside the image.
template <class Image, class Color>
inline void safePutPixel(int yc, int xc, const Color & col, Image *pim) {
if (!pim)
return;
if (pim->Contains(yc, xc)) {
(*pim)(yc, xc) = col;
}
}
/// Put the pixel in the image to the given color only if the point (xc,yc)
/// is inside the image. This function support multi-channel color
/// \note The color pointer col must have size as the image depth
template <class Image, class Color>
inline void safePutPixel(int yc, int xc, const Color *col, Image *pim) {
if (!pim)
return;
if (pim->Contains(yc, xc)) {
for (int i = 0; i < pim->Depth(); ++i)
(*pim)(yc, xc, i) = *(col + i);
}
}
// Bresenham approach to draw ellipse.
// http://raphaello.univ-fcomte.fr/ig/algorithme/ExemplesGLUt/BresenhamEllipse.htm
// Add the rotation of the ellipse.
// As the algo. use symmetry we must use 4 rotations.
template <class Image, class Color>
void DrawEllipse(int xc, int yc, int radiusA, int radiusB,
const Color &col, Image *pim, double angle = 0.0) {
int a = radiusA;
int b = radiusB;
// Counter Clockwise rotation matrix.
double matXY[4] = { cos(angle), sin(angle),
-sin(angle), cos(angle)};
int x, y;
double d1, d2;
x = 0;
y = b;
d1 = b*b - a*a*b + a*a/4;
float rotX = (matXY[0] * x + matXY[1] * y);
float rotY = (matXY[2] * x + matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
rotX = (matXY[0] * x - matXY[1] * y);
rotY = (matXY[2] * x - matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
rotX = (-matXY[0] * x - matXY[1] * y);
rotY = (-matXY[2] * x - matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
rotX = (-matXY[0] * x + matXY[1] * y);
rotY = (-matXY[2] * x + matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
while (a*a*(y-.5) > b*b*(x+1)) {
if (d1 < 0) {
d1 += b*b*(2*x+3);
++x;
} else {
d1 += b*b*(2*x+3) + a*a*(-2*y+2);
++x;
--y;
}
rotX = (matXY[0] * x + matXY[1] * y);
rotY = (matXY[2] * x + matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
rotX = (matXY[0] * x - matXY[1] * y);
rotY = (matXY[2] * x - matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
rotX = (-matXY[0] * x - matXY[1] * y);
rotY = (-matXY[2] * x - matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
rotX = (-matXY[0] * x + matXY[1] * y);
rotY = (-matXY[2] * x + matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
}
d2 = b*b*(x+.5)*(x+.5) + a*a*(y-1)*(y-1) - a*a*b*b;
while (y > 0) {
if (d2 < 0) {
d2 += b*b*(2*x+2) + a*a*(-2*y+3);
--y;
++x;
} else {
d2 += a*a*(-2*y+3);
--y;
}
rotX = (matXY[0] * x + matXY[1] * y);
rotY = (matXY[2] * x + matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
rotX = (matXY[0] * x - matXY[1] * y);
rotY = (matXY[2] * x - matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
rotX = (-matXY[0] * x - matXY[1] * y);
rotY = (-matXY[2] * x - matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
rotX = (-matXY[0] * x + matXY[1] * y);
rotY = (-matXY[2] * x + matXY[3] * y);
safePutPixel(yc + rotY, xc + rotX, col, pim);
}
}
// Bresenham approach do not allow to draw concentric circle without holes.
// So it's better the use the Andres method.
// http://fr.wikipedia.org/wiki/Algorithme_de_tracé_de_cercle_d'Andres.
template <class Image, class Color>
void DrawCircle(int x, int y, int radius, const Color &col, Image *pim) {
Image &im = *pim;
if ( im.Contains(y + radius, x + radius)
|| im.Contains(y + radius, x - radius)
|| im.Contains(y - radius, x + radius)
|| im.Contains(y - radius, x - radius)) {
int x1 = 0;
int y1 = radius;
int d = radius - 1;
while (y1 >= x1) {
// Draw the point for each octant.
safePutPixel( y1 + y, x1 + x, col, pim);
safePutPixel( x1 + y, y1 + x, col, pim);
safePutPixel( y1 + y, -x1 + x, col, pim);
safePutPixel( x1 + y, -y1 + x, col, pim);
safePutPixel(-y1 + y, x1 + x, col, pim);
safePutPixel(-x1 + y, y1 + x, col, pim);
safePutPixel(-y1 + y, -x1 + x, col, pim);
safePutPixel(-x1 + y, -y1 + x, col, pim);
if (d >= 2 * x1) {
d = d - 2 * x1 - 1;
x1 += 1;
} else {
if (d <= 2 * (radius - y1)) {
d = d + 2 * y1 - 1;
y1 -= 1;
} else {
d = d + 2 * (y1 - x1 - 1);
y1 -= 1;
x1 += 1;
}
}
}
}
}
// Bresenham algorithm
template <class Image, class Color>
void DrawLine(int xa, int ya, int xb, int yb, const Color &col, Image *pim) {
Image &im = *pim;
// If one point is outside the image
// Replace the outside point by the intersection of the line and
// the limit (either x=width or y=height).
if (!im.Contains(ya, xa) || !im.Contains(yb, xb)) {
int width = pim->Width();
int height = pim->Height();
const bool xdir = xa < xb, ydir = ya < yb;
float nx0 = xa, nx1 = xb, ny0 = ya, ny1 = yb,
&xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
&xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
&xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
&xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
if (xright < 0 || xleft >= width) return;
if (xleft < 0) {
yleft -= xleft*(yright - yleft)/(xright - xleft);
xleft = 0;
}
if (xright >= width) {
yright -= (xright - width)*(yright - yleft)/(xright - xleft);
xright = width - 1;
}
if (ydown < 0 || yup >= height) return;
if (yup < 0) {
xup -= yup*(xdown - xup)/(ydown - yup);
yup = 0;
}
if (ydown >= height) {
xdown -= (ydown - height)*(xdown - xup)/(ydown - yup);
ydown = height - 1;
}
xa = (int) xleft;
xb = (int) xright;
ya = (int) yleft;
yb = (int) yright;
}
int xbas, xhaut, ybas, yhaut;
// Check the condition ybas < yhaut.
if (ya <= yb) {
xbas = xa;
ybas = ya;
xhaut = xb;
yhaut = yb;
} else {
xbas = xb;
ybas = yb;
xhaut = xa;
yhaut = ya;
}
// Initialize slope.
int x, y, dx, dy, incrmX, incrmY, dp, N, S;
dx = xhaut - xbas;
dy = yhaut - ybas;
if (dx > 0) { // If xhaut > xbas we will increment X.
incrmX = 1;
} else {
incrmX = -1; // else we will decrement X.
dx *= -1;
}
if (dy > 0) { // Positive slope will increment X.
incrmY = 1;
} else { // Negative slope.
incrmY = -1;
}
if (dx >= dy) {
dp = 2 * dy - dx;
S = 2 * dy;
N = 2 * (dy - dx);
y = ybas;
x = xbas;
while (x != xhaut) {
safePutPixel(y, x, col, pim);
x += incrmX;
if (dp <= 0) { // Go in direction of the South Pixel.
dp += S;
} else { // Go to the North.
dp += N;
y+=incrmY;
}
}
} else {
dp = 2 * dx - dy;
S = 2 * dx;
N = 2 * (dx - dy);
x = xbas;
y = ybas;
while (y < yhaut) {
safePutPixel(y, x, col, pim);
y += incrmY;
if (dp <= 0) { // Go in direction of the South Pixel.
dp += S;
} else { // Go to the North.
dp += N;
x += incrmX;
}
}
}
safePutPixel(y, x, col, pim);
}
} // namespace libmv
#endif // LIBMV_IMAGE_IMAGE_DRAWING_H

45
extern/libmv/libmv/image/image_test.cc vendored Normal file
View File

@ -0,0 +1,45 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include <iostream>
#include "libmv/image/image.h"
#include "testing/testing.h"
using libmv::Image;
using libmv::Array3Df;
namespace {
TEST(Image, SimpleImageAccessors) {
Array3Df *array = new Array3Df(2, 3);
Image image(array);
EXPECT_EQ(array, image.AsArray3Df());
EXPECT_TRUE(NULL == image.AsArray3Du());
}
TEST(Image, MemorySizeInBytes) {
Array3Df *array = new Array3Df(2, 3);
Image image(array);
int size = sizeof(image) + array->MemorySizeInBytes();
EXPECT_EQ(size, image.MemorySizeInBytes());
}
} // namespace

89
extern/libmv/libmv/image/sample_test.cc vendored Normal file
View File

@ -0,0 +1,89 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/image/sample.h"
#include "testing/testing.h"
using namespace libmv;
namespace {
TEST(Image, Nearest) {
Array3Du image(2, 2);
image(0, 0) = 0;
image(0, 1) = 1;
image(1, 0) = 2;
image(1, 1) = 3;
EXPECT_EQ(0, SampleNearest(image, -0.4f, -0.4f));
EXPECT_EQ(0, SampleNearest(image, 0.4f, 0.4f));
EXPECT_EQ(3, SampleNearest(image, 0.6f, 0.6f));
EXPECT_EQ(3, SampleNearest(image, 1.4f, 1.4f));
}
TEST(Image, Linear) {
Array3Df image(2, 2);
image(0, 0) = 0;
image(0, 1) = 1;
image(1, 0) = 2;
image(1, 1) = 3;
EXPECT_EQ(1.5, SampleLinear(image, 0.5, 0.5));
}
TEST(Image, DownsampleBy2) {
Array3Df image(2, 2);
image(0, 0) = 0;
image(0, 1) = 1;
image(1, 0) = 2;
image(1, 1) = 3;
Array3Df resampled_image;
DownsampleChannelsBy2(image, &resampled_image);
ASSERT_EQ(1, resampled_image.Height());
ASSERT_EQ(1, resampled_image.Width());
ASSERT_EQ(1, resampled_image.Depth());
EXPECT_FLOAT_EQ(6./4., resampled_image(0, 0));
}
TEST(Image, DownsampleBy2MultiChannel) {
Array3Df image(2, 2, 3);
image(0, 0, 0) = 0;
image(0, 1, 0) = 1;
image(1, 0, 0) = 2;
image(1, 1, 0) = 3;
image(0, 0, 1) = 5;
image(0, 1, 1) = 6;
image(1, 0, 1) = 7;
image(1, 1, 1) = 8;
image(0, 0, 2) = 9;
image(0, 1, 2) = 10;
image(1, 0, 2) = 11;
image(1, 1, 2) = 12;
Array3Df resampled_image;
DownsampleChannelsBy2(image, &resampled_image);
ASSERT_EQ(1, resampled_image.Height());
ASSERT_EQ(1, resampled_image.Width());
ASSERT_EQ(3, resampled_image.Depth());
EXPECT_FLOAT_EQ((0+1+2+3)/4., resampled_image(0, 0, 0));
EXPECT_FLOAT_EQ((5+6+7+8)/4., resampled_image(0, 0, 1));
EXPECT_FLOAT_EQ((9+10+11+12)/4., resampled_image(0, 0, 2));
}
} // namespace

83
extern/libmv/libmv/image/tuple_test.cc vendored Normal file
View File

@ -0,0 +1,83 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/image/tuple.h"
#include "testing/testing.h"
#include <algorithm>
using libmv::Tuple;
namespace {
TEST(Tuple, InitConstantValue) {
Tuple<int, 3> t(5);
EXPECT_EQ(t(0), 5);
EXPECT_EQ(t(0), 5);
EXPECT_EQ(t(0), 5);
}
TEST(Tuple, InitFromPointer) {
float vals[3] = {1.0f, 2.0f, 3.0f};
Tuple<float, 3> t(vals);
for (int i = 0; i < 3; i++)
EXPECT_EQ(t(i), vals[i]);
Tuple<int, 3> b(t);
EXPECT_EQ(b(0), int(vals[0]));
EXPECT_EQ(b(1), int(vals[1]));
EXPECT_EQ(b(2), int(vals[2]));
}
TEST(Tuple, Swap) {
unsigned char vala[3] = {1, 2, 3};
unsigned char valb[3] = {4, 5, 6};
Tuple<unsigned char, 3> a(vala);
Tuple<unsigned char, 3> b(valb);
std::swap(a, b);
EXPECT_EQ(a(0), int(valb[0]));
EXPECT_EQ(a(1), int(valb[1]));
EXPECT_EQ(a(2), int(valb[2]));
EXPECT_EQ(b(0), int(vala[0]));
EXPECT_EQ(b(1), int(vala[1]));
EXPECT_EQ(b(2), int(vala[2]));
}
TEST(Tuple, IsEqualOperator) {
Tuple<int, 3> a;
a(0) = 1;
a(1) = 2;
a(2) = 3;
Tuple<int, 3> b;
b(0) = 1;
b(1) = 2;
b(2) = 3;
EXPECT_TRUE(a == b);
EXPECT_FALSE(a != b);
b(1) = 5;
EXPECT_TRUE(a != b);
EXPECT_FALSE(a == b);
}
} // namespace

View File

@ -0,0 +1,237 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/multiview/euclidean_resection.h"
#include "libmv/numeric/numeric.h"
#include "libmv/logging/logging.h"
#include "libmv/multiview/projection.h"
#include "testing/testing.h"
using namespace libmv::euclidean_resection;
using namespace libmv;
// Generates all necessary inputs and expected outputs for EuclideanResection.
void CreateCameraSystem(const Mat3& KK,
const Mat3X& x_image,
const Vec& X_distances,
const Mat3& R_input,
const Vec3& T_input,
Mat2X *x_camera,
Mat3X *X_world,
Mat3 *R_expected,
Vec3 *T_expected) {
int num_points = x_image.cols();
Mat3X x_unit_cam(3, num_points);
x_unit_cam = KK.inverse() * x_image;
// Create normalized camera coordinates to be used as an input to the PnP
// function, instead of using NormalizeColumnVectors(&x_unit_cam).
*x_camera = x_unit_cam.block(0, 0, 2, num_points);
for (int i = 0; i < num_points; ++i) {
x_unit_cam.col(i).normalize();
}
// Create the 3D points in the camera system.
Mat X_camera(3, num_points);
for (int i = 0; i < num_points; ++i) {
X_camera.col(i) = X_distances(i) * x_unit_cam.col(i);
}
// Apply the transformation to the camera 3D points
Mat translation_matrix(3, num_points);
translation_matrix.row(0).setConstant(T_input(0));
translation_matrix.row(1).setConstant(T_input(1));
translation_matrix.row(2).setConstant(T_input(2));
*X_world = R_input * X_camera + translation_matrix;
// Create the expected result for comparison.
*R_expected = R_input.transpose();
*T_expected = *R_expected * (-T_input);
};
TEST(AbsoluteOrientation, QuaternionSolution) {
int num_points = 4;
Mat X;
Mat Xp;
X = 100 * Mat::Random(3, num_points);
// Create a random translation and rotation.
Mat3 R_input;
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
Vec3 t_input;
t_input.setRandom();
t_input = 100 * t_input;
Mat translation_matrix(3, num_points);
translation_matrix.row(0).setConstant(t_input(0));
translation_matrix.row(1).setConstant(t_input(1));
translation_matrix.row(2).setConstant(t_input(2));
// Create the transformed 3D points Xp as Xp = R * X + t.
Xp = R_input * X + translation_matrix;
// Output variables.
Mat3 R;
Vec3 t;
AbsoluteOrientation(X, Xp, &R, &t);
EXPECT_MATRIX_NEAR(t, t_input, 1e-6);
EXPECT_MATRIX_NEAR(R, R_input, 1e-8);
}
TEST(EuclideanResection, Points4KnownImagePointsRandomTranslationRotation) {
// In this test only the translation and rotation are random. The image
// points are selected from a real case and are well conditioned.
Vec2i image_dimensions;
image_dimensions << 1600, 1200;
Mat3 KK;
KK << 2796, 0, 804,
0 , 2796, 641,
0, 0, 1;
// The real image points.
int num_points = 4;
Mat3X x_image(3, num_points);
x_image << 1164.06, 734.948, 749.599, 430.727,
681.386, 844.59, 496.315, 580.775,
1, 1, 1, 1;
// A vector of the 4 distances to the 3D points.
Vec X_distances = 100 * Vec::Random(num_points).array().abs();
// Create the random camera motion R and t that resection should recover.
Mat3 R_input;
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
Vec3 T_input;
T_input.setRandom();
T_input = 100 * T_input;
// Create the camera system, also getting the expected result of the
// transformation.
Mat3 R_expected;
Vec3 T_expected;
Mat3X X_world;
Mat2X x_camera;
CreateCameraSystem(KK, x_image, X_distances, R_input, T_input,
&x_camera, &X_world, &R_expected, &T_expected);
// Finally, run the code under test.
Mat3 R_output;
Vec3 T_output;
EuclideanResection(x_camera, X_world,
&R_output, &T_output,
RESECTION_ANSAR_DANIILIDIS);
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
// For now, the EPnP doesn't have a non-linear optimization step and so is
// not precise enough with only 4 points.
//
// TODO(jmichot): Reenable this test when there is nonlinear refinement.
#if 0
R_output.setIdentity();
T_output.setZero();
EuclideanResection(x_camera, X_world,
&R_output, &T_output,
RESECTION_EPNP);
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);*/
#endif
}
// TODO(jmichot): Reduce the code duplication here with the code above.
TEST(EuclideanResection, Points6AllRandomInput) {
Mat3 KK;
KK << 2796, 0, 804,
0 , 2796, 641,
0, 0, 1;
// Create random image points for a 1600x1200 image.
int w = 1600;
int h = 1200;
int num_points = 6;
Mat3X x_image(3, num_points);
x_image.row(0) = w * Vec::Random(num_points).array().abs();
x_image.row(1) = h * Vec::Random(num_points).array().abs();
x_image.row(2).setOnes();
// Normalized camera coordinates to be used as an input to the PnP function.
Mat2X x_camera;
Vec X_distances = 100 * Vec::Random(num_points).array().abs();
// Create the random camera motion R and t that resection should recover.
Mat3 R_input;
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
Vec3 T_input;
T_input.setRandom();
T_input = 100 * T_input;
// Create the camera system.
Mat3 R_expected;
Vec3 T_expected;
Mat3X X_world;
CreateCameraSystem(KK, x_image, X_distances, R_input, T_input,
&x_camera, &X_world, &R_expected, &T_expected);
// Test each of the resection methods.
{
Mat3 R_output;
Vec3 T_output;
EuclideanResection(x_camera, X_world,
&R_output, &T_output,
RESECTION_ANSAR_DANIILIDIS);
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
}
{
Mat3 R_output;
Vec3 T_output;
EuclideanResection(x_camera, X_world,
&R_output, &T_output,
RESECTION_EPNP);
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
}
{
Mat3 R_output;
Vec3 T_output;
EuclideanResection(x_image, X_world, KK,
&R_output, &T_output);
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
}
}

View File

@ -0,0 +1,162 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include <iostream>
#include "libmv/logging/logging.h"
#include "libmv/multiview/conditioning.h"
#include "libmv/multiview/fundamental.h"
#include "libmv/multiview/projection.h"
#include "libmv/multiview/test_data_sets.h"
#include "libmv/numeric/numeric.h"
#include "testing/testing.h"
namespace {
using namespace libmv;
TEST(Fundamental, FundamentalFromProjections) {
Mat34 P1_gt, P2_gt;
P1_gt << 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0;
P2_gt << 1, 1, 1, 3,
0, 2, 0, 3,
0, 1, 1, 0;
Mat3 F_gt;
FundamentalFromProjections(P1_gt, P2_gt, &F_gt);
Mat34 P1, P2;
ProjectionsFromFundamental(F_gt, &P1, &P2);
Mat3 F;
FundamentalFromProjections(P1, P2, &F);
EXPECT_MATRIX_PROP(F_gt, F, 1e-6);
}
TEST(Fundamental, PreconditionerFromPoints) {
int n = 4;
Mat points(2, n);
points << 0, 0, 1, 1,
0, 2, 1, 3;
Mat3 T;
PreconditionerFromPoints(points, &T);
Mat normalized_points;
ApplyTransformationToPoints(points, T, &normalized_points);
Vec mean, variance;
MeanAndVarianceAlongRows(normalized_points, &mean, &variance);
EXPECT_NEAR(0, mean(0), 1e-8);
EXPECT_NEAR(0, mean(1), 1e-8);
EXPECT_NEAR(2, variance(0), 1e-8);
EXPECT_NEAR(2, variance(1), 1e-8);
}
TEST(Fundamental, EssentialFromFundamental) {
TwoViewDataSet d = TwoRealisticCameras();
Mat3 E_from_Rt;
EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E_from_Rt);
Mat3 E_from_F;
EssentialFromFundamental(d.F, d.K1, d.K2, &E_from_F);
EXPECT_MATRIX_PROP(E_from_Rt, E_from_F, 1e-6);
}
TEST(Fundamental, MotionFromEssential) {
TwoViewDataSet d = TwoRealisticCameras();
Mat3 E;
EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E);
Mat3 R;
Vec3 t;
RelativeCameraMotion(d.R1, d.t1, d.R2, d.t2, &R, &t);
NormalizeL2(&t);
std::vector<Mat3> Rs;
std::vector<Vec3> ts;
MotionFromEssential(E, &Rs, &ts);
bool one_solution_is_correct = false;
for (size_t i = 0; i < Rs.size(); ++i) {
if (FrobeniusDistance(Rs[i], R) < 1e-8 && DistanceL2(ts[i], t) < 1e-8) {
one_solution_is_correct = true;
break;
}
}
EXPECT_TRUE(one_solution_is_correct);
}
TEST(Fundamental, MotionFromEssentialChooseSolution) {
TwoViewDataSet d = TwoRealisticCameras();
Mat3 E;
EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E);
Mat3 R;
Vec3 t;
RelativeCameraMotion(d.R1, d.t1, d.R2, d.t2, &R, &t);
NormalizeL2(&t);
std::vector<Mat3> Rs;
std::vector<Vec3> ts;
MotionFromEssential(E, &Rs, &ts);
Vec2 x1, x2;
MatrixColumn(d.x1, 0, &x1);
MatrixColumn(d.x2, 0, &x2);
int solution = MotionFromEssentialChooseSolution(Rs, ts, d.K1, x1, d.K2, x2);
EXPECT_LE(0, solution);
EXPECT_LE(solution, 3);
EXPECT_LE(FrobeniusDistance(Rs[solution], R), 1e-8);
EXPECT_LE(DistanceL2(ts[solution], t), 1e-8);
}
TEST(Fundamental, MotionFromEssentialAndCorrespondence) {
TwoViewDataSet d = TwoRealisticCameras();
Mat3 E;
EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E);
Mat3 R;
Vec3 t;
RelativeCameraMotion(d.R1, d.t1, d.R2, d.t2, &R, &t);
NormalizeL2(&t);
Vec2 x1, x2;
MatrixColumn(d.x1, 0, &x1);
MatrixColumn(d.x2, 0, &x2);
Mat3 R_estimated;
Vec3 t_estimated;
MotionFromEssentialAndCorrespondence(E, d.K1, x1, d.K2, x2,
&R_estimated, &t_estimated);
EXPECT_LE(FrobeniusDistance(R_estimated, R), 1e-8);
EXPECT_LE(DistanceL2(t_estimated, t), 1e-8);
}
} // namespace

View File

@ -0,0 +1,248 @@
// Copyright (c) 2011 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#ifndef LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_
#define LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_
#include "libmv/multiview/projection.h"
namespace libmv {
namespace homography {
namespace homography2D {
/**
* Structure for estimating the asymmetric error between a vector x2 and the
* transformed x1 such that
* Error = ||x2 - Psi(H * x1)||^2
* where Psi is the function that transforms homogeneous to euclidean coords.
* \note It should be distributed as Chi-squared with k = 2.
*/
struct AsymmetricError {
/**
* Computes the asymmetric residuals between a set of 2D points x2 and the
* transformed 2D point set x1 such that
* Residuals_i = x2_i - Psi(H * x1_i)
* where Psi is the function that transforms homogeneous to euclidean coords.
*
* \param[in] H The 3x3 homography matrix.
* The estimated homography should approximatelly hold the condition y = H x.
* \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
* \param[out] dx A 2xN matrix of column vectors of residuals errors
*/
static void Residuals(const Mat &H, const Mat &x1,
const Mat &x2, Mat2X *dx) {
dx->resize(2, x1.cols());
Mat3X x2h_est;
if (x1.rows() == 2)
x2h_est = H * EuclideanToHomogeneous(static_cast<Mat2X>(x1));
else
x2h_est = H * x1;
dx->row(0) = x2h_est.row(0).array() / x2h_est.row(2).array();
dx->row(1) = x2h_est.row(1).array() / x2h_est.row(2).array();
if (x2.rows() == 2)
*dx = x2 - *dx;
else
*dx = HomogeneousToEuclidean(static_cast<Mat3X>(x2)) - *dx;
}
/**
* Computes the asymmetric residuals between a 2D point x2 and the transformed
* 2D point x1 such that
* Residuals = x2 - Psi(H * x1)
* where Psi is the function that transforms homogeneous to euclidean coords.
*
* \param[in] H The 3x3 homography matrix.
* The estimated homography should approximatelly hold the condition y = H x.
* \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
* \param[out] dx A vector of size 2 of the residual error
*/
static void Residuals(const Mat &H, const Vec &x1,
const Vec &x2, Vec2 *dx) {
Vec3 x2h_est;
if (x1.rows() == 2)
x2h_est = H * EuclideanToHomogeneous(static_cast<Vec2>(x1));
else
x2h_est = H * x1;
if (x2.rows() == 2)
*dx = x2 - x2h_est.head<2>() / x2h_est[2];
else
*dx = HomogeneousToEuclidean(static_cast<Vec3>(x2)) -
x2h_est.head<2>() / x2h_est[2];
}
/**
* Computes the squared norm of the residuals between a set of 2D points x2
* and the transformed 2D point set x1 such that
* Error = || x2 - Psi(H * x1) ||^2
* where Psi is the function that transforms homogeneous to euclidean coords.
*
* \param[in] H The 3x3 homography matrix.
* The estimated homography should approximatelly hold the condition y = H x.
* \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
* \return The squared norm of the asymmetric residuals errors
*/
static double Error(const Mat &H, const Mat &x1, const Mat &x2) {
Mat2X dx;
Residuals(H, x1, x2, &dx);
return dx.squaredNorm();
}
/**
* Computes the squared norm of the residuals between a 2D point x2 and the
* transformed 2D point x1 such that rms = || x2 - Psi(H * x1) ||^2
* where Psi is the function that transforms homogeneous to euclidean coords.
*
* \param[in] H The 3x3 homography matrix.
* The estimated homography should approximatelly hold the condition y = H x.
* \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
* \return The squared norm of the asymmetric residual error
*/
static double Error(const Mat &H, const Vec &x1, const Vec &x2) {
Vec2 dx;
Residuals(H, x1, x2, &dx);
return dx.squaredNorm();
}
};
/**
* Structure for estimating the symmetric error
* between a vector x2 and the transformed x1 such that
* Error = ||x2 - Psi(H * x1)||^2 + ||x1 - Psi(H^-1 * x2)||^2
* where Psi is the function that transforms homogeneous to euclidean coords.
* \note It should be distributed as Chi-squared with k = 4.
*/
struct SymmetricError {
/**
* Computes the squared norm of the residuals between x2 and the
* transformed x1 such that
* Error = ||x2 - Psi(H * x1)||^2 + ||x1 - Psi(H^-1 * x2)||^2
* where Psi is the function that transforms homogeneous to euclidean coords.
*
* \param[in] H The 3x3 homography matrix.
* The estimated homography should approximatelly hold the condition y = H x.
* \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
* \return The squared norm of the symmetric residuals errors
*/
static double Error(const Mat &H, const Vec &x1, const Vec &x2) {
// TODO(keir): This is awesomely inefficient because it does a 3x3
// inversion for each evaluation.
Mat3 Hinv = H.inverse();
return AsymmetricError::Error(H, x1, x2) +
AsymmetricError::Error(Hinv, x2, x1);
}
// TODO(julien) Add residuals function \see AsymmetricError
};
/**
* Structure for estimating the algebraic error (cross product)
* between a vector x2 and the transformed x1 such that
* Error = ||[x2] * H * x1||^^2
* where [x2] is the skew matrix of x2.
*/
struct AlgebraicError {
// TODO(julien) Make an AlgebraicError2Rows and AlgebraicError3Rows
/**
* Computes the algebraic residuals (cross product) between a set of 2D
* points x2 and the transformed 2D point set x1 such that
* [x2] * H * x1 where [x2] is the skew matrix of x2.
*
* \param[in] H The 3x3 homography matrix.
* The estimated homography should approximatelly hold the condition y = H x.
* \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
* \param[out] dx A 3xN matrix of column vectors of residuals errors
*/
static void Residuals(const Mat &H, const Mat &x1,
const Mat &x2, Mat3X *dx) {
dx->resize(3, x1.cols());
Vec3 col;
for (int i = 0; i < x1.cols(); ++i) {
Residuals(H, x1.col(i), x2.col(i), &col);
dx->col(i) = col;
}
}
/**
* Computes the algebraic residuals (cross product) between a 2D point x2
* and the transformed 2D point x1 such that
* [x2] * H * x1 where [x2] is the skew matrix of x2.
*
* \param[in] H The 3x3 homography matrix.
* The estimated homography should approximatelly hold the condition y = H x.
* \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
* \param[out] dx A vector of size 3 of the residual error
*/
static void Residuals(const Mat &H, const Vec &x1,
const Vec &x2, Vec3 *dx) {
Vec3 x2h_est;
if (x1.rows() == 2)
x2h_est = H * EuclideanToHomogeneous(static_cast<Vec2>(x1));
else
x2h_est = H * x1;
if (x2.rows() == 2)
*dx = SkewMat(EuclideanToHomogeneous(static_cast<Vec2>(x2))) * x2h_est;
else
*dx = SkewMat(x2) * x2h_est;
// TODO(julien) This is inefficient since it creates an
// identical 3x3 skew matrix for each evaluation.
}
/**
* Computes the squared norm of the algebraic residuals between a set of 2D
* points x2 and the transformed 2D point set x1 such that
* [x2] * H * x1 where [x2] is the skew matrix of x2.
*
* \param[in] H The 3x3 homography matrix.
* The estimated homography should approximatelly hold the condition y = H x.
* \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
* \return The squared norm of the asymmetric residuals errors
*/
static double Error(const Mat &H, const Mat &x1, const Mat &x2) {
Mat3X dx;
Residuals(H, x1, x2, &dx);
return dx.squaredNorm();
}
/**
* Computes the squared norm of the algebraic residuals between a 2D point x2
* and the transformed 2D point x1 such that
* [x2] * H * x1 where [x2] is the skew matrix of x2.
*
* \param[in] H The 3x3 homography matrix.
* The estimated homography should approximatelly hold the condition y = H x.
* \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
* \return The squared norm of the asymmetric residual error
*/
static double Error(const Mat &H, const Vec &x1, const Vec &x2) {
Vec3 dx;
Residuals(H, x1, x2, &dx);
return dx.squaredNorm();
}
};
// TODO(keir): Add error based on ideal points.
} // namespace homography2D
// TODO(julien) add homography3D errors
} // namespace homography
} // namespace libmv
#endif // LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_

View File

@ -0,0 +1,261 @@
// Copyright (c) 2011 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "testing/testing.h"
#include "libmv/logging/logging.h"
#include "libmv/multiview/projection.h"
#include "libmv/multiview/homography.h"
namespace {
using namespace libmv;
namespace {
// Check whether homography transform M actually transforms
// given vectors x1 to x2. Used to check validness of a reconstructed
// homography matrix.
// TODO(sergey): Consider using this in all tests since possible homography
// matrix is not fixed to a single value and different-looking matrices
// might actually crrespond to the same exact transform.
void CheckHomography2DTransform(const Mat3 &H,
const Mat &x1,
const Mat &x2) {
for (int i = 0; i < x2.cols(); ++i) {
Vec3 x2_expected = x2.col(i);
Vec3 x2_observed = H * x1.col(i);
x2_observed /= x2_observed(2);
EXPECT_MATRIX_NEAR(x2_expected, x2_observed, 1e-8);
}
}
} // namespace
TEST(Homography2DTest, Rotation45AndTranslationXY) {
Mat x1(3, 4);
x1 << 0, 1, 0, 5,
0, 0, 2, 3,
1, 1, 1, 1;
double angle = 45.0;
Mat3 m;
m << cos(angle), -sin(angle), -2,
sin(angle), cos(angle), 5,
0, 0, 1;
Mat x2 = x1;
// Transform point from ground truth matrix
for (int i = 0; i < x2.cols(); ++i)
x2.col(i) = m * x1.col(i);
Mat3 homography_mat;
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
VLOG(1) << "Mat Homography2D ";
VLOG(1) << homography_mat;
VLOG(1) << "Mat GT ";
VLOG(1) << m;
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
}
TEST(Homography2DTest, AffineGeneral4) {
// TODO(julien) find why it doesn't work with 4 points!!!
Mat x1(3, 4);
x1 << 0, 1, 0, 2,
0, 0, 1, 2,
1, 1, 1, 1;
Mat3 m;
m << 3, -1, 4,
6, -2, -3,
0, 0, 1;
Mat x2 = x1;
for (int i = 0; i < x2.cols(); ++i) {
x2.col(i) = m * x1.col(i);
}
Mat3 homography_mat;
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
VLOG(1) << "Mat Homography2D";
VLOG(1) << homography_mat;
CheckHomography2DTransform(homography_mat, x1, x2);
// Test with euclidean coordinates
Mat eX1, eX2;
HomogeneousToEuclidean(x1, &eX1);
HomogeneousToEuclidean(x2, &eX2);
homography_mat.setIdentity();
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(eX1, eX2, &homography_mat));
VLOG(1) << "Mat Homography2D ";
VLOG(1) << homography_mat;
CheckHomography2DTransform(homography_mat, x1, x2);
}
TEST(Homography2DTest, AffineGeneral5) {
Mat x1(3, 5);
x1 << 0, 1, 0, 2, 5,
0, 0, 1, 2, 2,
1, 1, 1, 1, 1;
Mat3 m;
m << 3, -1, 4,
6, -2, -3,
0, 0, 1;
Mat x2 = x1;
for (int i = 0; i < x2.cols(); ++i)
x2.col(i) = m * x1.col(i);
Mat3 homography_mat;
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
VLOG(1) << "Mat Homography2D ";
VLOG(1) << homography_mat;
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
// Test with euclidean coordinates
Mat eX1, eX2;
HomogeneousToEuclidean(x1, &eX1);
HomogeneousToEuclidean(x2, &eX2);
homography_mat.setIdentity();
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(eX1, eX2, &homography_mat));
VLOG(1) << "Mat Homography2D ";
VLOG(1) << homography_mat;
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
}
TEST(Homography2DTest, HomographyGeneral) {
Mat x1(3, 4);
x1 << 0, 1, 0, 5,
0, 0, 2, 3,
1, 1, 1, 1;
Mat3 m;
m << 3, -1, 4,
6, -2, -3,
1, -3, 1;
Mat x2 = x1;
for (int i = 0; i < x2.cols(); ++i)
x2.col(i) = m * x1.col(i);
Mat3 homography_mat;
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
VLOG(1) << "Mat Homography2D ";
VLOG(1) << homography_mat;
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
}
TEST(Homography3DTest, RotationAndTranslationXYZ) {
Mat x1(4, 5);
x1 << 0, 0, 1, 5, 2,
0, 1, 2, 3, 5,
0, 2, 0, 1, 5,
1, 1, 1, 1, 1;
Mat4 M;
M.setIdentity();
/*
M = AngleAxisd(45.0, Vector3f::UnitZ())
* AngleAxisd(25.0, Vector3f::UnitX())
* AngleAxisd(5.0, Vector3f::UnitZ());*/
// Rotation on x + translation
double angle = 45.0;
Mat4 rot;
rot << 1, 0, 0, 1,
0, cos(angle), -sin(angle), 3,
0, sin(angle), cos(angle), -2,
0, 0, 0, 1;
M *= rot;
// Rotation on y
angle = 25.0;
rot << cos(angle), 0, sin(angle), 0,
0, 1, 0, 0,
-sin(angle), 0, cos(angle), 0,
0, 0, 0, 1;
M *= rot;
// Rotation on z
angle = 5.0;
rot << cos(angle), -sin(angle), 0, 0,
sin(angle), cos(angle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
M *= rot;
Mat x2 = x1;
for (int i = 0; i < x2.cols(); ++i) {
x2.col(i) = M * x1.col(i);
}
Mat4 homography_mat;
EXPECT_TRUE(Homography3DFromCorrespondencesLinear(x1, x2, &homography_mat));
VLOG(1) << "Mat Homography3D " << homography_mat;
VLOG(1) << "Mat GT " << M;
EXPECT_MATRIX_NEAR(homography_mat, M, 1e-8);
}
TEST(Homography3DTest, AffineGeneral) {
Mat x1(4, 5);
x1 << 0, 0, 1, 5, 2,
0, 1, 2, 3, 5,
0, 2, 0, 1, 5,
1, 1, 1, 1, 1;
Mat4 m;
m << 3, -1, 4, 1,
6, -2, -3, -6,
1, 0, 1, 2,
0, 0, 0, 1;
Mat x2 = x1;
for (int i = 0; i < x2.cols(); ++i) {
x2.col(i) = m * x1.col(i);
}
Mat4 homography_mat;
EXPECT_TRUE(Homography3DFromCorrespondencesLinear(x1, x2, &homography_mat));
VLOG(1) << "Mat Homography3D ";
VLOG(1) << homography_mat;
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
}
TEST(Homography3DTest, HomographyGeneral) {
Mat x1(4, 5);
x1 << 0, 0, 1, 5, 2,
0, 1, 2, 3, 5,
0, 2, 0, 1, 5,
1, 1, 1, 1, 1;
Mat4 m;
m << 3, -1, 4, 1,
6, -2, -3, -6,
1, 0, 1, 2,
-3, 1, 0, 1;
Mat x2 = x1;
for (int i = 0; i < x2.cols(); ++i) {
x2.col(i) = m * x1.col(i);
}
Mat4 homography_mat;
EXPECT_TRUE(Homography3DFromCorrespondencesLinear(x1, x2, &homography_mat));
VLOG(1) << "Mat Homography3D";
VLOG(1) << homography_mat;
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
}
} // namespace

View File

@ -0,0 +1,94 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include <iostream>
#include "libmv/base/vector.h"
#include "libmv/logging/logging.h"
#include "libmv/multiview/nviewtriangulation.h"
#include "libmv/multiview/projection.h"
#include "libmv/multiview/test_data_sets.h"
#include "libmv/numeric/numeric.h"
#include "testing/testing.h"
namespace {
using namespace libmv;
TEST(NViewTriangulate, FiveViews) {
int nviews = 5;
int npoints = 6;
NViewDataSet d = NRealisticCamerasFull(nviews, npoints);
// Collect P matrices together.
vector<Mat34> Ps(nviews);
for (int j = 0; j < nviews; ++j) {
Ps[j] = d.P(j);
}
for (int i = 0; i < npoints; ++i) {
// Collect the image of point i in each frame.
Mat2X xs(2, nviews);
for (int j = 0; j < nviews; ++j) {
xs.col(j) = d.x[j].col(i);
}
Vec4 X;
NViewTriangulate(xs, Ps, &X);
// Check reprojection error. Should be nearly zero.
for (int j = 0; j < nviews; ++j) {
Vec3 x_reprojected = Ps[j]*X;
x_reprojected /= x_reprojected(2);
double error = (x_reprojected.head(2) - xs.col(j)).norm();
EXPECT_NEAR(error, 0.0, 1e-9);
}
}
}
TEST(NViewTriangulateAlgebraic, FiveViews) {
int nviews = 5;
int npoints = 6;
NViewDataSet d = NRealisticCamerasFull(nviews, npoints);
// Collect P matrices together.
vector<Mat34> Ps(nviews);
for (int j = 0; j < nviews; ++j) {
Ps[j] = d.P(j);
}
for (int i = 0; i < npoints; ++i) {
// Collect the image of point i in each frame.
Mat2X xs(2, nviews);
for (int j = 0; j < nviews; ++j) {
xs.col(j) = d.x[j].col(i);
}
Vec4 X;
NViewTriangulate(xs, Ps, &X);
// Check reprojection error. Should be nearly zero.
for (int j = 0; j < nviews; ++j) {
Vec3 x_reprojected = Ps[j]*X;
x_reprojected /= x_reprojected(2);
double error = (x_reprojected.head<2>() - xs.col(j)).norm();
EXPECT_NEAR(error, 0.0, 1e-9);
}
}
}
} // namespace

View File

@ -0,0 +1,51 @@
// Copyright (c) 2008, 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/multiview/panography_kernel.h"
#include "libmv/multiview/panography.h"
namespace libmv {
namespace panography {
namespace kernel {
void TwoPointSolver::Solve(const Mat &x1, const Mat &x2, vector<Mat3> *Hs) {
// Solve for the focal lengths.
vector<double> fs;
F_FromCorrespondance_2points(x1, x2, &fs);
// Then solve for the rotations and homographies.
Mat x1h, x2h;
EuclideanToHomogeneous(x1, &x1h);
EuclideanToHomogeneous(x2, &x2h);
for (int i = 0; i < fs.size(); ++i) {
Mat3 K1 = Mat3::Identity() * fs[i];
K1(2, 2) = 1.0;
Mat3 R;
GetR_FixedCameraCenter(x1h, x2h, fs[i], &R);
R /= R(2, 2);
(*Hs).push_back(K1 * R * K1.inverse());
}
}
} // namespace kernel
} // namespace panography
} // namespace libmv

View File

@ -0,0 +1,54 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#ifndef LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H
#define LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H
#include "libmv/base/vector.h"
#include "libmv/multiview/conditioning.h"
#include "libmv/multiview/projection.h"
#include "libmv/multiview/two_view_kernel.h"
#include "libmv/multiview/homography_error.h"
#include "libmv/numeric/numeric.h"
namespace libmv {
namespace panography {
namespace kernel {
struct TwoPointSolver {
enum { MINIMUM_SAMPLES = 2 };
static void Solve(const Mat &x1, const Mat &x2, vector<Mat3> *Hs);
};
typedef two_view::kernel::Kernel<
TwoPointSolver, homography::homography2D::AsymmetricError, Mat3>
UnnormalizedKernel;
typedef two_view::kernel::Kernel<
two_view::kernel::NormalizedSolver<TwoPointSolver, UnnormalizerI>,
homography::homography2D::AsymmetricError,
Mat3>
Kernel;
} // namespace kernel
} // namespace panography
} // namespace libmv
#endif // LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H

View File

@ -0,0 +1,144 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/logging/logging.h"
#include "libmv/multiview/panography.h"
#include "libmv/multiview/panography_kernel.h"
#include "libmv/multiview/projection.h"
#include "libmv/numeric/numeric.h"
#include "testing/testing.h"
namespace libmv {
namespace {
TEST(Panography, PrintSomeSharedFocalEstimationValues) {
Mat x1(2, 2), x2(2, 2);
x1<< 158, 78,
124, 113;
x2<< 300, 214,
125, 114;
// Normalize data (set principal point 0,0 and image border to 1.0).
x1.block<1, 2>(0, 0) /= 320;
x1.block<1, 2>(1, 0) /= 240;
x2.block<1, 2>(0, 0) /= 320;
x2.block<1, 2>(1, 0) /= 240;
x1+=Mat2::Constant(0.5);
x2+=Mat2::Constant(0.5);
vector<double> fs;
F_FromCorrespondance_2points(x1, x2, &fs);
// Assert we found a valid solution.
EXPECT_EQ(1, fs.size());
EXPECT_NEAR(1.01667, fs[1], 1e-3);
}
TEST(Panography, GetR_FixedCameraCenterWithIdentity) {
Mat x1(3, 3);
x1 << 0.5, 0.6, 0.7,
0.5, 0.5, 0.4,
10.0, 10.0, 10.0;
Mat3 R;
GetR_FixedCameraCenter(x1, x1, 1.0, &R);
R /= R(2, 2);
EXPECT_MATRIX_NEAR(Mat3::Identity(), R, 1e-8);
LOG(INFO) << "R \n" << R;
}
TEST(Panography, Homography_GetR_Test_PitchY30) {
int n = 3;
Mat x1(3, n);
x1 << 0.5, 0.6, 0.7,
0.5, 0.5, 0.4,
10, 10, 10;
Mat x2 = x1;
const double alpha = 30.0 * M_PI / 180.0;
Mat3 rotY;
rotY << cos(alpha), 0, -sin(alpha),
0, 1, 0,
sin(alpha), 0, cos(alpha);
for (int i = 0; i < n; ++i) {
x2.block<3, 1>(0, i) = rotY * x1.col(i);
}
Mat3 R;
GetR_FixedCameraCenter(x1, x2, 1.0, &R);
// Assert that residuals are small enough
for (int i = 0; i < n; ++i) {
Vec residuals = (R * x1.col(i)) - x2.col(i);
EXPECT_NEAR(0, residuals.norm(), 1e-6);
}
// Check that the rotation angle along Y is the expected one.
// Use the euler approximation to recover the angle.
double pitch_y = asin(R(2, 0)) * 180.0 / M_PI;
EXPECT_NEAR(30, pitch_y, 1e-4);
}
TEST(MinimalPanoramic, Real_Case_Kernel) {
const int n = 2;
Mat x1(2, n); // From image 0.jpg
x1<< 158, 78,
124, 113;
Mat x2(2, n); // From image 3.jpg
x2<< 300, 214,
125, 114;
Mat3 Ground_TruthHomography;
Ground_TruthHomography<< 1, 0.02, 129.83,
-0.02, 1.012, 0.07823,
0, 0, 1;
vector<Mat3> Hs;
libmv::panography::kernel::TwoPointSolver::Solve(x1, x2, &Hs);
LOG(INFO) << "Got " << Hs.size() << " solutions.";
for (int j = 0; j < Hs.size(); ++j) {
Mat3 H = Hs[j];
EXPECT_MATRIX_NEAR(H, Ground_TruthHomography, 1e-1);
Mat x1h, x2h;
EuclideanToHomogeneous(x1, &x1h);
EuclideanToHomogeneous(x2, &x2h);
// Assert that residuals are small enough
for (int i = 0; i < n; ++i) {
Vec x1p = H * x1h.col(i);
Vec residuals = x1p/x1p(2) - x2h.col(i);
EXPECT_MATRIX_NEAR_ZERO(residuals, 1e-5);
}
}
}
} // namespace
} // namespace libmv
// TODO(pmoulon): Add a real test case based on images.
// TODO(pmoulon): Add a check for the actual f value for the real images.
// TODO(pmoulon): Add a test that has some inliers and outliers.

View File

@ -0,0 +1,115 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include <iostream>
#include "libmv/multiview/projection.h"
#include "libmv/numeric/numeric.h"
#include "testing/testing.h"
namespace {
using namespace libmv;
TEST(Projection, P_From_KRt) {
Mat3 K, Kp;
K << 10, 1, 30,
0, 20, 40,
0, 0, 1;
Mat3 R, Rp;
R << 1, 0, 0,
0, 1, 0,
0, 0, 1;
Vec3 t, tp;
t << 1, 2, 3;
Mat34 P;
P_From_KRt(K, R, t, &P);
KRt_From_P(P, &Kp, &Rp, &tp);
EXPECT_MATRIX_NEAR(K, Kp, 1e-8);
EXPECT_MATRIX_NEAR(R, Rp, 1e-8);
EXPECT_MATRIX_NEAR(t, tp, 1e-8);
// TODO(keir): Change the code to ensure det(R) == 1, which is not currently
// the case. Also add a test for that here.
}
Vec4 GetRandomPoint() {
Vec4 X;
X.setRandom();
X(3) = 1;
return X;
}
TEST(Projection, isInFrontOfCamera) {
Mat34 P;
P << 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0;
Vec4 X_front = GetRandomPoint();
Vec4 X_back = GetRandomPoint();
X_front(2) = 10; // Any point in the positive Z direction
// where Z > 1 is infront of the camera.
X_back(2) = -10; // Any point int he negative Z dirstaion
// is behind the camera.
bool res_front = isInFrontOfCamera(P, X_front);
bool res_back = isInFrontOfCamera(P, X_back);
EXPECT_EQ(true, res_front);
EXPECT_EQ(false, res_back);
}
TEST(AutoCalibration, ProjectionShiftPrincipalPoint) {
Mat34 P1, P2;
P1 << 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0;
P2 << 1, 0, 3, 0,
0, 1, 4, 0,
0, 0, 1, 0;
Mat34 P1_computed, P2_computed;
ProjectionShiftPrincipalPoint(P1, Vec2(0, 0), Vec2(3, 4), &P2_computed);
ProjectionShiftPrincipalPoint(P2, Vec2(3, 4), Vec2(0, 0), &P1_computed);
EXPECT_MATRIX_EQ(P1, P1_computed);
EXPECT_MATRIX_EQ(P2, P2_computed);
}
TEST(AutoCalibration, ProjectionChangeAspectRatio) {
Mat34 P1, P2;
P1 << 1, 0, 3, 0,
0, 1, 4, 0,
0, 0, 1, 0;
P2 << 1, 0, 3, 0,
0, 2, 4, 0,
0, 0, 1, 0;
Mat34 P1_computed, P2_computed;
ProjectionChangeAspectRatio(P1, Vec2(3, 4), 1, 2, &P2_computed);
ProjectionChangeAspectRatio(P2, Vec2(3, 4), 2, 1, &P1_computed);
EXPECT_MATRIX_EQ(P1, P1_computed);
EXPECT_MATRIX_EQ(P2, P2_computed);
}
} // namespace

View File

@ -0,0 +1,61 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include <iostream>
#include "libmv/multiview/projection.h"
#include "libmv/multiview/resection.h"
#include "libmv/multiview/test_data_sets.h"
#include "libmv/numeric/numeric.h"
#include "testing/testing.h"
#include "libmv/logging/logging.h"
namespace {
using namespace libmv;
using namespace libmv::resection;
TEST(Resection, ThreeViews) {
int nviews = 5;
int npoints = 6;
NViewDataSet d = NRealisticCamerasFull(nviews, npoints);
for (int i = 0; i < nviews; ++i) {
Mat4X X(4, npoints);
X.block(0, 0, 3, npoints) = d.X;
X.row(3).setOnes();
const Mat2X &x = d.x[i];
Mat34 P;
Resection(x, X, &P);
Mat34 P_expected = d.P(i);
// Because the P matrices are homogeneous, it is necessary to be tricky
// about the scale factor to make them match.
P_expected *= 1/P_expected.array().abs().sum();
P *= 1/P.array().abs().sum();
if (!((P(0, 0) > 0 && P_expected(0, 0) > 0) ||
(P(0, 0) < 0 && P_expected(0, 0) < 0))) {
P *= -1;
}
EXPECT_MATRIX_NEAR(P_expected, P, 1e-9);
}
}
} // namespace

View File

@ -0,0 +1,196 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/multiview/test_data_sets.h"
#include <cmath>
#include "libmv/numeric/numeric.h"
#include "libmv/multiview/projection.h"
#include "libmv/multiview/fundamental.h"
namespace libmv {
TwoViewDataSet TwoRealisticCameras(bool same_K) {
TwoViewDataSet d;
d.K1 << 320, 0, 160,
0, 320, 120,
0, 0, 1;
if (same_K) {
d.K2 = d.K1;
} else {
d.K2 << 360, 0, 170,
0, 360, 110,
0, 0, 1;
}
d.R1 = RotationAroundZ(-0.1);
d.R2 = RotationAroundX(-0.1);
d.t1 << 1, 1, 10;
d.t2 << -2, -1, 10;
P_From_KRt(d.K1, d.R1, d.t1, &d.P1);
P_From_KRt(d.K2, d.R2, d.t2, &d.P2);
FundamentalFromProjections(d.P1, d.P2, &d.F);
d.X.resize(3, 30);
d.X.setRandom();
Project(d.P1, d.X, &d.x1);
Project(d.P2, d.X, &d.x2);
return d;
}
nViewDatasetConfigator::nViewDatasetConfigator(int fx , int fy,
int cx, int cy,
double distance,
double jitter_amount) {
_fx = fx;
_fy = fy;
_cx = cx;
_cy = cy;
_dist = distance;
_jitter_amount = jitter_amount;
}
NViewDataSet NRealisticCamerasFull(int nviews, int npoints,
const nViewDatasetConfigator config) {
NViewDataSet d;
d.n = nviews;
d.K.resize(nviews);
d.R.resize(nviews);
d.t.resize(nviews);
d.C.resize(nviews);
d.x.resize(nviews);
d.x_ids.resize(nviews);
d.X.resize(3, npoints);
d.X.setRandom();
d.X *= 0.6;
Vecu all_point_ids(npoints);
for (size_t j = 0; j < npoints; ++j)
all_point_ids[j] = j;
for (size_t i = 0; i < nviews; ++i) {
Vec3 camera_center, t, jitter, lookdir;
double theta = i * 2 * M_PI / nviews;
camera_center << sin(theta), 0.0, cos(theta);
camera_center *= config._dist;
d.C[i] = camera_center;
jitter.setRandom();
jitter *= config._jitter_amount / camera_center.norm();
lookdir = -camera_center + jitter;
d.K[i] << config._fx, 0, config._cx,
0, config._fy, config._cy,
0, 0, 1;
d.R[i] = LookAt(lookdir);
d.t[i] = -d.R[i] * camera_center;
d.x[i] = Project(d.P(i), d.X);
d.x_ids[i] = all_point_ids;
}
return d;
}
NViewDataSet NRealisticCamerasSparse(int nviews, int npoints,
float view_ratio, unsigned min_projections,
const nViewDatasetConfigator config) {
assert(view_ratio <= 1.0);
assert(view_ratio > 0.0);
assert(min_projections <= npoints);
NViewDataSet d;
d.n = nviews;
d.K.resize(nviews);
d.R.resize(nviews);
d.t.resize(nviews);
d.C.resize(nviews);
d.x.resize(nviews);
d.x_ids.resize(nviews);
d.X.resize(3, npoints);
d.X.setRandom();
d.X *= 0.6;
Mat visibility(nviews, npoints);
visibility.setZero();
Mat randoms(nviews, npoints);
randoms.setRandom();
randoms = (randoms.array() + 1)/2.0;
unsigned num_visibles = 0;
for (size_t i = 0; i < nviews; ++i) {
num_visibles = 0;
for (size_t j = 0; j < npoints; j++) {
if (randoms(i, j) <= view_ratio) {
visibility(i, j) = true;
num_visibles++;
}
}
if (num_visibles < min_projections) {
unsigned num_projections_to_add = min_projections - num_visibles;
for (size_t j = 0; j < npoints && num_projections_to_add > 0; ++j) {
if (!visibility(i, j)) {
num_projections_to_add--;
}
}
num_visibles += num_projections_to_add;
}
d.x_ids[i].resize(num_visibles);
d.x[i].resize(2, num_visibles);
}
size_t j_visible = 0;
Vec3 X;
for (size_t i = 0; i < nviews; ++i) {
Vec3 camera_center, t, jitter, lookdir;
double theta = i * 2 * M_PI / nviews;
camera_center << sin(theta), 0.0, cos(theta);
camera_center *= config._dist;
d.C[i] = camera_center;
jitter.setRandom();
jitter *= config._jitter_amount / camera_center.norm();
lookdir = -camera_center + jitter;
d.K[i] << config._fx, 0, config._cx,
0, config._fy, config._cy,
0, 0, 1;
d.R[i] = LookAt(lookdir);
d.t[i] = -d.R[i] * camera_center;
j_visible = 0;
for (size_t j = 0; j < npoints; j++) {
if (visibility(i, j)) {
X = d.X.col(j);
d.x[i].col(j_visible) = Project(d.P(i), X);
d.x_ids[i][j_visible] = j;
j_visible++;
}
}
}
return d;
}
} // namespace libmv

View File

@ -0,0 +1,105 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#ifndef LIBMV_MULTIVIEW_TEST_DATA_SETS_H_
#define LIBMV_MULTIVIEW_TEST_DATA_SETS_H_
#include "libmv/base/vector.h"
#include "libmv/multiview/fundamental.h"
#include "libmv/multiview/projection.h"
#include "libmv/numeric/numeric.h"
namespace libmv {
struct TwoViewDataSet {
Mat3 K1, K2; // Internal parameters.
Mat3 R1, R2; // Rotation.
Vec3 t1, t2; // Translation.
Mat34 P1, P2; // Projection matrix, P = K(R|t)
Mat3 F; // Fundamental matrix.
Mat3X X; // 3D points.
Mat2X x1, x2; // Projected points.
};
// Two cameras at (-1,-1,-10) and (2,1,-10) looking approximately towards z+.
TwoViewDataSet TwoRealisticCameras(bool same_K = false);
// An N-view metric dataset . An important difference between this
// and the other reconstruction data types is that all points are seen by all
// cameras.
struct NViewDataSet {
vector<Mat3> K; // Internal parameters (fx, fy, etc).
vector<Mat3> R; // Rotation.
vector<Vec3> t; // Translation.
vector<Vec3> C; // Camera centers.
Mat3X X; // 3D points.
vector<Mat2X> x; // Projected points; may have noise added.
vector<Vecu> x_ids; // Indexes of points corresponding to the projections
int n; // Actual number of cameras.
Mat34 P(int i) {
assert(i < n);
return K[i] * HStack(R[i], t[i]);
}
Mat3 F(int i, int j) {
Mat3 F_;
FundamentalFromProjections(P(i), P(j), &F_);
return F_;
}
void Reproject() {
for (int i = 0; i < n; ++i) {
x[i] = Project(P(i), X);
}
}
// TODO(keir): Add gaussian jitter functions.
};
struct nViewDatasetConfigator {
/// Internal camera parameters
int _fx;
int _fy;
int _cx;
int _cy;
/// Camera random position parameters
double _dist;
double _jitter_amount;
nViewDatasetConfigator(int fx = 1000, int fy = 1000,
int cx = 500, int cy = 500,
double distance = 1.5,
double jitter_amount = 0.01);
};
NViewDataSet NRealisticCamerasFull(int nviews, int npoints,
const nViewDatasetConfigator
config = nViewDatasetConfigator());
// Generates sparse projections (not all points are projected)
NViewDataSet NRealisticCamerasSparse(int nviews, int npoints,
float view_ratio = 0.6,
unsigned min_projections = 3,
const nViewDatasetConfigator
config = nViewDatasetConfigator());
} // namespace libmv
#endif // LIBMV_MULTIVIEW_TEST_DATA_SETS_H_

View File

@ -0,0 +1,47 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include <iostream>
#include "libmv/multiview/triangulation.h"
#include "libmv/multiview/fundamental.h"
#include "libmv/multiview/projection.h"
#include "libmv/multiview/test_data_sets.h"
#include "libmv/numeric/numeric.h"
#include "testing/testing.h"
namespace {
using namespace libmv;
TEST(Triangulation, TriangulateDLT) {
TwoViewDataSet d = TwoRealisticCameras();
for (int i = 0; i < d.X.cols(); ++i) {
Vec2 x1, x2;
MatrixColumn(d.x1, i, &x1);
MatrixColumn(d.x2, i, &x2);
Vec3 X_estimated, X_gt;
MatrixColumn(d.X, i, &X_gt);
TriangulateDLT(d.P1, x1, d.P2, x2, &X_estimated);
EXPECT_NEAR(0, DistanceLInfinity(X_estimated, X_gt), 1e-8);
}
}
} // namespace

View File

@ -0,0 +1,137 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#ifndef LIBMV_MULTIVIEW_TWO_VIEW_KERNEL_H_
#define LIBMV_MULTIVIEW_TWO_VIEW_KERNEL_H_
#include "libmv/base/vector.h"
#include "libmv/logging/logging.h"
#include "libmv/multiview/conditioning.h"
#include "libmv/numeric/numeric.h"
namespace libmv {
namespace two_view {
namespace kernel {
template<typename Solver, typename Unnormalizer>
struct NormalizedSolver {
enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
static void Solve(const Mat &x1, const Mat &x2, vector<Mat3> *models) {
assert(2 == x1.rows());
assert(MINIMUM_SAMPLES <= x1.cols());
assert(x1.rows() == x2.rows());
assert(x1.cols() == x2.cols());
// Normalize the data.
Mat3 T1, T2;
Mat x1_normalized, x2_normalized;
NormalizePoints(x1, &x1_normalized, &T1);
NormalizePoints(x2, &x2_normalized, &T2);
Solver::Solve(x1_normalized, x2_normalized, models);
for (int i = 0; i < models->size(); ++i) {
Unnormalizer::Unnormalize(T1, T2, &(*models)[i]);
}
}
};
template<typename Solver, typename Unnormalizer>
struct IsotropicNormalizedSolver {
enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
static void Solve(const Mat &x1, const Mat &x2, vector<Mat3> *models) {
assert(2 == x1.rows());
assert(MINIMUM_SAMPLES <= x1.cols());
assert(x1.rows() == x2.rows());
assert(x1.cols() == x2.cols());
// Normalize the data.
Mat3 T1, T2;
Mat x1_normalized, x2_normalized;
NormalizeIsotropicPoints(x1, &x1_normalized, &T1);
NormalizeIsotropicPoints(x2, &x2_normalized, &T2);
Solver::Solve(x1_normalized, x2_normalized, models);
for (int i = 0; i < models->size(); ++i) {
Unnormalizer::Unnormalize(T1, T2, &(*models)[i]);
}
}
};
// This is one example (targeted at solvers that operate on correspondences
// between two views) that shows the "kernel" part of a robust fitting
// problem:
//
// 1. The model; Mat3 in the case of the F or H matrix.
// 2. The minimum number of samples needed to fit; 7 or 8 (or 4).
// 3. A way to convert samples to a model.
// 4. A way to convert a sample and a model to an error.
//
// Of particular note is that the kernel does not expose what the samples are.
// All the robust fitting algorithm sees is that there is some number of
// samples; it is able to fit subsets of them (via the kernel) and check their
// error, but can never access the samples themselves.
//
// The Kernel objects must follow the following concept so that the robust
// fitting alogrithm can fit this type of relation:
//
// 1. Kernel::Model
// 2. Kernel::MINIMUM_SAMPLES
// 3. Kernel::Fit(vector<int>, vector<Kernel::Model> *)
// 4. Kernel::Error(int, Model) -> error
//
// The fit routine must not clear existing entries in the vector of models; it
// should append new solutions to the end.
template<typename SolverArg,
typename ErrorArg,
typename ModelArg = Mat3>
class Kernel {
public:
Kernel(const Mat &x1, const Mat &x2) : x1_(x1), x2_(x2) {}
typedef SolverArg Solver;
typedef ModelArg Model;
enum { MINIMUM_SAMPLES = Solver::MINIMUM_SAMPLES };
void Fit(const vector<int> &samples, vector<Model> *models) const {
Mat x1 = ExtractColumns(x1_, samples);
Mat x2 = ExtractColumns(x2_, samples);
Solver::Solve(x1, x2, models);
}
double Error(int sample, const Model &model) const {
return ErrorArg::Error(model,
static_cast<Vec>(x1_.col(sample)),
static_cast<Vec>(x2_.col(sample)));
}
int NumSamples() const {
return x1_.cols();
}
static void Solve(const Mat &x1, const Mat &x2, vector<Model> *models) {
// By offering this, Kernel types can be passed to templates.
Solver::Solve(x1, x2, models);
}
protected:
const Mat &x1_;
const Mat &x2_;
};
} // namespace kernel
} // namespace two_view
} // namespace libmv
#endif // LIBMV_MULTIVIEW_TWO_VIEW_KERNEL_H_

View File

@ -0,0 +1,95 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "testing/testing.h"
#include "libmv/numeric/dogleg.h"
using namespace libmv;
namespace {
class F {
public:
typedef Vec4 FMatrixType;
typedef Vec3 XMatrixType;
Vec4 operator()(const Vec3 &x) const {
double x1 = x.x() - 2;
double y1 = x.y() - 5;
double z1 = x.z();
Vec4 fx; fx << x1*x1 + z1*z1,
y1*y1 + z1*z1,
z1*z1,
x1*x1;
return fx;
}
};
TEST(Dogleg, SimpleCase) {
Vec3 x; x << 0.76026643, -30.01799744, 0.55192142;
F f;
Dogleg<F>::SolverParameters params;
Dogleg<F> lm(f);
/* TODO(sergey): Better error handling. */
/* Dogleg<F>::Results results = */ lm.minimize(params, &x);
Vec3 expected_min_x; expected_min_x << 2, 5, 0;
EXPECT_MATRIX_NEAR(expected_min_x, x, 1e-5);
}
// Example 3.2 from [1]; page 11 of the pdf, 20 of the document. This is a
// tricky problem because of the singluar Jacobian near the origin.
class F32 {
public:
typedef Vec2 FMatrixType;
typedef Vec2 XMatrixType;
Vec2 operator()(const Vec2 &x) const {
double x1 = x(0);
double x2 = 10*x(0)/(x(0) + 0.1) + 2*x(1)*x(1);
Vec2 fx; fx << x1, x2;
return fx;
}
};
class JF32 {
public:
JF32(const F32 &f) { (void) f; }
Mat2 operator()(const Vec2 &x) {
Mat2 J; J << 1, 0,
1./pow(x(0) + 0.1, 2), 4*x(1)*x(1);
return J;
}
};
// TODO(keir): Re-enable this when the dogleg code properly handles singular
// normal equations.
/*
TEST(Dogleg, Example32) {
Vec2 x; x << 3, 1;
F32 f;
CheckJacobian<F32, JF32>(f, x);
Dogleg<F32, JF32> dogleg(f);
Dogleg<F32, JF32>::Results results = dogleg.minimize(&x);
Vec2 expected_min_x; expected_min_x << 0, 0;
EXPECT_MATRIX_NEAR(expected_min_x, x, 1e-5);
}
*/
} // namespace

View File

@ -0,0 +1,57 @@
// Copyright (c) 2007, 2008, 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "testing/testing.h"
#include "libmv/numeric/numeric.h"
#include "libmv/numeric/function_derivative.h"
using namespace libmv;
namespace {
class F {
public:
typedef Vec2 FMatrixType;
typedef Vec3 XMatrixType;
Vec2 operator()(const Vec3 &x) const {
Vec2 fx;
fx << 0.19*x(0) + 0.19*x(1)*x(1) + x(2),
3*sin(x(0)) + 2*cos(x(1));
return fx;
}
Mat23 J(const Vec3 &x) const {
Mat23 jacobian;
jacobian << 0.19, 2*0.19*x(1), 1.0,
3*cos(x(0)), -2*sin(x(1)), 0;
return jacobian;
}
};
TEST(FunctionDerivative, SimpleCase) {
Vec3 x; x << 0.76026643, 0.01799744, 0.55192142;
F f;
NumericJacobian<F, CENTRAL> J(f);
EXPECT_MATRIX_NEAR(f.J(x), J(x), 1e-8);
NumericJacobian<F, FORWARD> J_forward(f);
// Forward difference is very inaccurate.
EXPECT_MATRIX_NEAR(f.J(x), J_forward(x), 1e-5);
}
} // namespace

View File

@ -0,0 +1,56 @@
// Copyright (c) 2007, 2008, 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "testing/testing.h"
#include "libmv/numeric/levenberg_marquardt.h"
using namespace libmv;
namespace {
class F {
public:
typedef Vec4 FMatrixType;
typedef Vec3 XMatrixType;
Vec4 operator()(const Vec3 &x) const {
double x1 = x.x() - 2;
double y1 = x.y() - 5;
double z1 = x.z();
Vec4 fx; fx << x1*x1 + z1*z1,
y1*y1 + z1*z1,
z1*z1,
x1*x1;
return fx;
}
};
TEST(LevenbergMarquardt, SimpleCase) {
Vec3 x(0.76026643, -30.01799744, 0.55192142);
F f;
LevenbergMarquardt<F>::SolverParameters params;
LevenbergMarquardt<F> lm(f);
/* TODO(sergey): Better error handling. */
/* LevenbergMarquardt<F>::Results results = */ lm.minimize(params, &x);
Vec3 expected_min_x(2, 5, 0);
EXPECT_MATRIX_NEAR(expected_min_x, x, 1e-5);
}
} // namespace

View File

@ -0,0 +1,439 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/numeric/numeric.h"
#include "testing/testing.h"
using namespace libmv;
namespace {
TEST(Numeric, DynamicSizedNullspace) {
Mat A(3, 4);
A << 0.76026643, 0.01799744, 0.55192142, 0.8699745,
0.42016166, 0.97863392, 0.33711682, 0.14479271,
0.51016811, 0.66528302, 0.54395496, 0.57794893;
Vec x;
double s = Nullspace(&A, &x);
EXPECT_NEAR(0.0, s, 1e-15);
EXPECT_NEAR(0.0, (A * x).norm(), 1e-15);
EXPECT_NEAR(1.0, x.norm(), 1e-15);
}
TEST(Numeric, FixedSizeMatrixNullspace) {
Mat34 A;
A << 0.76026643, 0.01799744, 0.55192142, 0.8699745,
0.42016166, 0.97863392, 0.33711682, 0.14479271,
0.51016811, 0.66528302, 0.54395496, 0.57794893;
Vec x;
double s = Nullspace(&A, &x);
EXPECT_NEAR(0.0, s, 1e-15);
EXPECT_NEAR(0.0, (A * x).norm(), 1e-15);
EXPECT_NEAR(1.0, x.norm(), 1e-15);
}
TEST(Numeric, NullspaceMatchesLapackSVD) {
Mat43 A;
A << 0.76026643, 0.01799744, 0.55192142,
0.8699745, 0.42016166, 0.97863392,
0.33711682, 0.14479271, 0.51016811,
0.66528302, 0.54395496, 0.57794893;
Vec x;
double s = Nullspace(&A, &x);
EXPECT_NEAR(1.0, x.norm(), 1e-15);
EXPECT_NEAR(0.206694992663, s, 1e-9);
EXPECT_NEAR(0.206694992663, (A * x).norm(), 1e-9);
EXPECT_NEAR(-0.64999717, x(0), 1e-8);
EXPECT_NEAR(-0.18452646, x(1), 1e-8);
EXPECT_NEAR(0.7371931, x(2), 1e-8);
}
TEST(Numeric, Nullspace2) {
Mat43 A;
A << 0.76026643, 0.01799744, 0.55192142,
0.8699745, 0.42016166, 0.97863392,
0.33711682, 0.14479271, 0.51016811,
0.66528302, 0.54395496, 0.57794893;
Vec3 x1, x2;
double s = Nullspace2(&A, &x1, &x2);
EXPECT_NEAR(1.0, x1.norm(), 1e-15);
EXPECT_NEAR(0.206694992663, s, 1e-9);
EXPECT_NEAR(0.206694992663, (A * x1).norm(), 1e-9);
EXPECT_NEAR(-0.64999717, x1(0), 1e-8);
EXPECT_NEAR(-0.18452646, x1(1), 1e-8);
EXPECT_NEAR( 0.7371931, x1(2), 1e-8);
if (x2(0) < 0) {
x2 *= -1;
}
EXPECT_NEAR( 0.34679618, x2(0), 1e-8);
EXPECT_NEAR(-0.93519689, x2(1), 1e-8);
EXPECT_NEAR( 0.07168809, x2(2), 1e-8);
}
TEST(Numeric, TinyMatrixSquareTranspose) {
Mat2 A;
A << 1.0, 2.0, 3.0, 4.0;
libmv::TransposeInPlace(&A);
EXPECT_EQ(1.0, A(0, 0));
EXPECT_EQ(3.0, A(0, 1));
EXPECT_EQ(2.0, A(1, 0));
EXPECT_EQ(4.0, A(1, 1));
}
TEST(Numeric, NormalizeL1) {
Vec2 x;
x << 1, 2;
double l1 = NormalizeL1(&x);
EXPECT_DOUBLE_EQ(3., l1);
EXPECT_DOUBLE_EQ(1./3., x(0));
EXPECT_DOUBLE_EQ(2./3., x(1));
}
TEST(Numeric, NormalizeL2) {
Vec2 x;
x << 1, 2;
double l2 = NormalizeL2(&x);
EXPECT_DOUBLE_EQ(sqrt(5.0), l2);
EXPECT_DOUBLE_EQ(1./sqrt(5.), x(0));
EXPECT_DOUBLE_EQ(2./sqrt(5.), x(1));
}
TEST(Numeric, Diag) {
Vec x(2);
x << 1, 2;
Mat D = Diag(x);
EXPECT_EQ(1, D(0, 0));
EXPECT_EQ(0, D(0, 1));
EXPECT_EQ(0, D(1, 0));
EXPECT_EQ(2, D(1, 1));
}
TEST(Numeric, Determinant) {
Mat A(2, 2);
A << 1, 2,
-1, 3;
double detA = A.determinant();
EXPECT_NEAR(5, detA, 1e-8);
Mat B(4, 4);
B << 0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15;
double detB = B.determinant();
EXPECT_NEAR(0, detB, 1e-8);
Mat3 C;
C << 0, 1, 2,
3, 4, 5,
6, 7, 1;
double detC = C.determinant();
EXPECT_NEAR(21, detC, 1e-8);
}
TEST(Numeric, Inverse) {
Mat A(2, 2), A1;
A << 1, 2,
-1, 3;
Mat I = A * A.inverse();
EXPECT_NEAR(1, I(0, 0), 1e-8);
EXPECT_NEAR(0, I(0, 1), 1e-8);
EXPECT_NEAR(0, I(1, 0), 1e-8);
EXPECT_NEAR(1, I(1, 1), 1e-8);
Mat B(4, 4), B1;
B << 0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 2, 11,
12, 13, 14, 4;
Mat I2 = B * B.inverse();
EXPECT_NEAR(1, I2(0, 0), 1e-8);
EXPECT_NEAR(0, I2(0, 1), 1e-8);
EXPECT_NEAR(0, I2(0, 2), 1e-8);
EXPECT_NEAR(0, I2(1, 0), 1e-8);
EXPECT_NEAR(1, I2(1, 1), 1e-8);
EXPECT_NEAR(0, I2(1, 2), 1e-8);
EXPECT_NEAR(0, I2(2, 0), 1e-8);
EXPECT_NEAR(0, I2(2, 1), 1e-8);
EXPECT_NEAR(1, I2(2, 2), 1e-8);
}
TEST(Numeric, MeanAndVarianceAlongRows) {
int n = 4;
Mat points(2, n);
points << 0, 0, 1, 1,
0, 2, 1, 3;
Vec mean, variance;
MeanAndVarianceAlongRows(points, &mean, &variance);
EXPECT_NEAR(0.5, mean(0), 1e-8);
EXPECT_NEAR(1.5, mean(1), 1e-8);
EXPECT_NEAR(0.25, variance(0), 1e-8);
EXPECT_NEAR(1.25, variance(1), 1e-8);
}
TEST(Numeric, HorizontalStack) {
Mat x(2, 1), y(2, 1), z;
x << 1, 2;
y << 3, 4;
HorizontalStack(x, y, &z);
EXPECT_EQ(2, z.cols());
EXPECT_EQ(2, z.rows());
EXPECT_EQ(1, z(0, 0));
EXPECT_EQ(2, z(1, 0));
EXPECT_EQ(3, z(0, 1));
EXPECT_EQ(4, z(1, 1));
}
TEST(Numeric, HStack) {
Mat x(2, 1), y(2, 1), z(2, 2);
x << 1, 2;
y << 3, 4;
z << 1, 3,
2, 4;
Vec2 xC = x, yC = y;
Mat2 xy = HStack(x, y);
EXPECT_MATRIX_EQ(z, xy);
EXPECT_MATRIX_EQ(z, HStack(x, y));
EXPECT_MATRIX_EQ(z, HStack(x, yC));
EXPECT_MATRIX_EQ(z, HStack(xC, y));
EXPECT_MATRIX_EQ(z, HStack(xC, yC));
}
// TODO(keir): Need some way of verifying that the compile time types of the
// resulting stacked matrices properly propagate the fixed dimensions.
TEST(Numeric, VStack) {
Mat x(2, 2), y(2, 2), z(4, 2);
x << 1, 2,
3, 4;
y << 10, 20,
30, 40;
z << 1, 2,
3, 4,
10, 20,
30, 40;
Mat2 xC = x, yC = y;
Mat xy = VStack(x, y);
EXPECT_MATRIX_EQ(z, xy);
EXPECT_MATRIX_EQ(z, VStack(x, y));
EXPECT_MATRIX_EQ(z, VStack(x, yC));
EXPECT_MATRIX_EQ(z, VStack(xC, y));
EXPECT_MATRIX_EQ(z, VStack(xC, yC));
}
TEST(Numeric, VerticalStack) {
Mat x(1, 2), y(1, 2), z;
x << 1, 2;
y << 3, 4;
VerticalStack(x, y, &z);
EXPECT_EQ(2, z.cols());
EXPECT_EQ(2, z.rows());
EXPECT_EQ(1, z(0, 0));
EXPECT_EQ(2, z(0, 1));
EXPECT_EQ(3, z(1, 0));
EXPECT_EQ(4, z(1, 1));
}
TEST(Numeric, CrossProduct) {
Vec3 x, y, z;
x << 1, 0, 0;
y << 0, 1, 0;
z << 0, 0, 1;
Vec3 xy = CrossProduct(x, y);
Vec3 yz = CrossProduct(y, z);
Vec3 zx = CrossProduct(z, x);
EXPECT_NEAR(0, DistanceLInfinity(xy, z), 1e-8);
EXPECT_NEAR(0, DistanceLInfinity(yz, x), 1e-8);
EXPECT_NEAR(0, DistanceLInfinity(zx, y), 1e-8);
}
TEST(Numeric, CrossProductMatrix) {
Vec3 x, y;
x << 1, 2, 3;
y << 2, 3, 4;
Vec3 xy = CrossProduct(x, y);
Vec3 yx = CrossProduct(y, x);
Mat3 X = CrossProductMatrix(x);
Vec3 Xy, Xty;
Xy = X * y;
Xty = X.transpose() * y;
EXPECT_NEAR(0, DistanceLInfinity(xy, Xy), 1e-8);
EXPECT_NEAR(0, DistanceLInfinity(yx, Xty), 1e-8);
}
TEST(Numeric, MatrixColumn) {
Mat A2(2, 3);
Vec2 v2;
A2 << 1, 2, 3,
4, 5, 6;
MatrixColumn(A2, 1, &v2);
EXPECT_EQ(2, v2(0));
EXPECT_EQ(5, v2(1));
Mat A3(3, 3);
Vec3 v3;
A3 << 1, 2, 3,
4, 5, 6,
7, 8, 9;
MatrixColumn(A3, 1, &v3);
EXPECT_EQ(2, v3(0));
EXPECT_EQ(5, v3(1));
EXPECT_EQ(8, v3(2));
Mat A4(4, 3);
Vec4 v4;
A4 << 1, 2, 3,
4, 5, 6,
7, 8, 9,
10, 11, 12;
MatrixColumn(A4, 1, &v4);
EXPECT_EQ( 2, v4(0));
EXPECT_EQ( 5, v4(1));
EXPECT_EQ( 8, v4(2));
EXPECT_EQ(11, v4(3));
}
// This used to give a compile error with FLENS.
TEST(Numeric, TinyMatrixView) {
Mat34 P;
Mat K = P.block(0, 0, 3, 3);
}
// This gives a compile error.
TEST(Numeric, Mat3MatProduct) {
Mat3 A;
Mat3 B;
Mat C = A * B;
}
// This gives a compile error.
TEST(Numeric, Vec3Negative) {
Vec3 y; y << 1, 2, 3;
Vec3 x = -y;
EXPECT_EQ(-1, x(0));
EXPECT_EQ(-2, x(1));
EXPECT_EQ(-3, x(2));
}
// This gives a compile error.
TEST(Numeric, Vec3VecInteroperability) {
Vec y(3);
y << 1, 2, 3;
Vec3 x = y + y;
EXPECT_EQ(2, x(0));
EXPECT_EQ(4, x(1));
EXPECT_EQ(6, x(2));
}
// This segfaults inside lapack.
TEST(Numeric, DeterminantLU7) {
Mat A(5, 5);
A << 1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0,
0, 0, 0, 0, 1;
EXPECT_NEAR(1, A.determinant(), 1e-8);
}
// This segfaults inside lapack.
TEST(Numeric, DeterminantLU) {
Mat A(2, 2);
A << 1, 2,
-1, 3;
EXPECT_NEAR(5, A.determinant(), 1e-8);
}
// This does unexpected things.
// Keir: Not with eigen2!
TEST(Numeric, InplaceProduct) {
Mat2 K, S;
K << 1, 0,
0, 1;
S << 1, 0,
0, 1;
K = K * S;
EXPECT_MATRIX_NEAR(Mat2::Identity(), K, 1e-8);
}
TEST(Numeric, ExtractColumns) {
Mat2X A(2, 5);
A << 1, 2, 3, 4, 5,
6, 7, 8, 9, 10;
Vec2i columns; columns << 0, 2;
Mat2X extracted = ExtractColumns(A, columns);
EXPECT_NEAR(1, extracted(0, 0), 1e-15);
EXPECT_NEAR(3, extracted(0, 1), 1e-15);
EXPECT_NEAR(6, extracted(1, 0), 1e-15);
EXPECT_NEAR(8, extracted(1, 1), 1e-15);
}
TEST(Numeric, RotationRodrigues) {
Vec3 x, y, z;
x << 1, 0, 0;
y << 0, 1, 0;
z << 0, 0, 1;
Mat3 rodrigues_x = RotationRodrigues(x);
Mat3 rodrigues_y = RotationRodrigues(y);
Mat3 rodrigues_z = RotationRodrigues(z);
Mat3 Rx = RotationAroundX(1);
Mat3 Ry = RotationAroundY(1);
Mat3 Rz = RotationAroundZ(1);
EXPECT_MATRIX_NEAR(Rx, rodrigues_x, 1e-15);
EXPECT_MATRIX_NEAR(Ry, rodrigues_y, 1e-15);
EXPECT_MATRIX_NEAR(Rz, rodrigues_z, 1e-15);
}
TEST(Numeric, LookAt) {
// Simple orthogonality check.
Vec3 e; e << 1, 2, 3;
Mat3 R = LookAt(e), I = Mat3::Identity();
Mat3 RRT = R*R.transpose();
Mat3 RTR = R.transpose()*R;
EXPECT_MATRIX_NEAR(I, RRT, 1e-15);
EXPECT_MATRIX_NEAR(I, RTR, 1e-15);
}
TEST(Numeric, Reshape) {
Vec4 x; x << 1, 2, 3, 4;
Mat2 M, M_expected;
reshape(x, 2, 2, &M);
M_expected << 1, 2,
3, 4;
EXPECT_MATRIX_NEAR(M_expected, M, 1e-15);
}
} // namespace

98
extern/libmv/libmv/numeric/poly_test.cc vendored Normal file
View File

@ -0,0 +1,98 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/numeric/numeric.h"
#include "libmv/numeric/poly.h"
#include "testing/testing.h"
using namespace libmv;
namespace {
// Find the polynomial coefficients of x in the equation
//
// (x - a)(x - b)(x - c) == 0
//
// by expanding to
//
// x^3 - (c+b+a) * x^2 + (a*b+(b+a)*c) * x - a*b*c = 0.
// = p = q = r
void CoeffsForCubicZeros(double a, double b, double c,
double *p, double *q, double *r) {
*p = -(c + b + a);
*q = (a * b + (b + a) * c);
*r = -a * b * c;
}
// Find the polynomial coefficients of x in the equation
//
// (x - a)(x - b)(x - c)(x - d) == 0
//
// by expanding to
//
// x^4 - (d+c+b+a) * x^3 + (d*(c+b+a) + a*b+(b+a)*c) * x^2
// - (d*(a*b+(b+a)*c)+a*b*c) * x + a*b*c*d = 0.
void CoeffsForQuarticZeros(double a, double b, double c, double d,
double *p, double *q, double *r, double *s) {
*p = -(d + c + b + a);
*q = (d * (c + b + a) + a * b + (b + a) * c);
*r = -(d * (a * b + (b + a) * c) + a * b * c);
*s = a * b * c *d;
}
TEST(Poly, SolveCubicPolynomial) {
double a, b, c, aa, bb, cc;
double p, q, r;
a = 1; b = 2; c = 3;
CoeffsForCubicZeros(a, b, c, &p, &q, &r);
ASSERT_EQ(3, SolveCubicPolynomial(p, q, r, &aa, &bb, &cc));
EXPECT_NEAR(a, aa, 1e-10);
EXPECT_NEAR(b, bb, 1e-10);
EXPECT_NEAR(c, cc, 1e-10);
a = 0; b = 1; c = 3;
CoeffsForCubicZeros(a, b, c, &p, &q, &r);
ASSERT_EQ(3, SolveCubicPolynomial(p, q, r, &aa, &bb, &cc));
EXPECT_NEAR(a, aa, 1e-10);
EXPECT_NEAR(b, bb, 1e-10);
EXPECT_NEAR(c, cc, 1e-10);
a = -10; b = 0; c = 1;
CoeffsForCubicZeros(a, b, c, &p, &q, &r);
ASSERT_EQ(3, SolveCubicPolynomial(p, q, r, &aa, &bb, &cc));
EXPECT_NEAR(a, aa, 1e-10);
EXPECT_NEAR(b, bb, 1e-10);
EXPECT_NEAR(c, cc, 1e-10);
a = -8; b = 1; c = 3;
CoeffsForCubicZeros(a, b, c, &p, &q, &r);
ASSERT_EQ(3, SolveCubicPolynomial(p, q, r, &aa, &bb, &cc));
EXPECT_NEAR(a, aa, 1e-10);
EXPECT_NEAR(b, bb, 1e-10);
EXPECT_NEAR(c, cc, 1e-10);
a = 28; b = 28; c = 105;
CoeffsForCubicZeros(a, b, c, &p, &q, &r);
ASSERT_EQ(3, SolveCubicPolynomial(p, q, r, &aa, &bb, &cc));
EXPECT_NEAR(a, aa, 1e-10);
EXPECT_NEAR(b, bb, 1e-10);
EXPECT_NEAR(c, cc, 1e-10);
}
} // namespace

View File

@ -0,0 +1,239 @@
// Copyright (c) 2011 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/simple_pipeline/camera_intrinsics.h"
#include <iostream>
#include "testing/testing.h"
#include "libmv/image/image.h"
#include "libmv/image/image_drawing.h"
#include "libmv/logging/logging.h"
namespace libmv {
TEST(PolynomialCameraIntrinsics2, ApplyOnFocalCenter) {
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(1300.0, 1300.0);
intrinsics.SetPrincipalPoint(640.0, 540.0);
intrinsics.SetRadialDistortion(-0.2, -0.1, -0.05);
double distorted_x, distorted_y;
intrinsics.ApplyIntrinsics(0.0, 0.0, &distorted_x, &distorted_y);
EXPECT_NEAR(640.0, distorted_x, 1e-8);
EXPECT_NEAR(540.0, distorted_y, 1e-8);
}
TEST(PolynomialCameraIntrinsics, InvertOnFocalCenter) {
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(1300.0, 1300.0);
intrinsics.SetPrincipalPoint(640.0, 540.0);
intrinsics.SetRadialDistortion(-0.2, -0.1, -0.05);
double normalized_x, normalized_y;
intrinsics.InvertIntrinsics(640.0, 540.0, &normalized_x, &normalized_y);
EXPECT_NEAR(0.0, normalized_x, 1e-8);
EXPECT_NEAR(0.0, normalized_y, 1e-8);
}
TEST(PolynomialCameraIntrinsics, ApplyIntrinsics) {
const int N = 5;
double expected[N][N][2] = {
{ {75.312500, -24.687500}, {338.982239, -62.035522},
{640.000000, -72.929688}, {941.017761, -62.035522},
{1204.687500, -24.687500}},
{ {37.964478, 238.982239}, {323.664551, 223.664551},
{640.000000, 219.193420}, {956.335449, 223.664551},
{1242.035522, 238.982239}},
{ {27.070312, 540.000000}, {319.193420, 540.000000},
{640.000000, 540.000000}, {960.806580, 540.000000},
{1252.929688, 540.000000}},
{ {37.964478, 841.017761}, {323.664551, 856.335449},
{640.000000, 860.806580}, {956.335449, 856.335449},
{1242.035522, 841.017761}},
{ {75.312500, 1104.687500}, {338.982239, 1142.035522},
{640.000000, 1152.929688}, {941.017761, 1142.035522},
{1204.687500, 1104.687500}}
};
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(1300.0, 1300.0);
intrinsics.SetPrincipalPoint(640.0, 540.0);
intrinsics.SetRadialDistortion(-0.2, -0.1, -0.05);
double step = 1.0 / (N - 1);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
double normalized_x = j * step - 0.5,
normalized_y = i * step - 0.5;
double distorted_x, distorted_y;
intrinsics.ApplyIntrinsics(normalized_x, normalized_y,
&distorted_x, &distorted_y);
EXPECT_NEAR(expected[i][j][0], distorted_x, 1e-6);
EXPECT_NEAR(expected[i][j][1], distorted_y, 1e-6);
}
}
}
TEST(PolynomialCameraIntrinsics, InvertIntrinsics) {
const int N = 5;
double expected[N][N][2] = {
{ {-0.524482, -0.437069}, {-0.226237, -0.403994},
{ 0.031876, -0.398446}, { 0.293917, -0.408218},
{ 0.632438, -0.465028}},
{ {-0.493496, -0.189173}, {-0.219052, -0.179936},
{ 0.030975, -0.178107}, { 0.283742, -0.181280},
{ 0.574557, -0.194335}},
{ {-0.488013, 0.032534}, {-0.217537, 0.031077},
{ 0.030781, 0.030781}, { 0.281635, 0.031293},
{ 0.566344, 0.033314}},
{ {-0.498696, 0.257660}, {-0.220424, 0.244041},
{ 0.031150, 0.241409}, { 0.285660, 0.245985},
{ 0.582670, 0.265629}},
{ {-0.550617, 0.532263}, {-0.230399, 0.477255},
{ 0.032380, 0.469510}, { 0.299986, 0.483311},
{ 0.684740, 0.584043}}
};
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(1300.0, 1300.0);
intrinsics.SetPrincipalPoint(600.0, 500.0);
intrinsics.SetRadialDistortion(-0.2, -0.1, -0.05);
double step_x = 1280.0 / (N - 1),
step_y = 1080.0 / (N - 1);
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
double distorted_x = j * step_x,
distorted_y = i * step_y;
double normalized_x, normalized_y;
intrinsics.InvertIntrinsics(distorted_x, distorted_y,
&normalized_x, &normalized_y);
EXPECT_NEAR(expected[i][j][0], normalized_x, 1e-6);
EXPECT_NEAR(expected[i][j][1], normalized_y, 1e-6);
}
}
}
TEST(PolynomialCameraIntrinsics, ApplyIsInvertibleSimple) {
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(1300.0, 1300.0);
intrinsics.SetPrincipalPoint(640.0, 540.0);
intrinsics.SetRadialDistortion(-0.2, -0.1, -0.05);
// Scan over image coordinates, invert the intrinsics, then re-apply them to
// make sure the cycle gets back where it started.
for (double y = 0; y < 1000; y += 100) {
for (double x = 0; x < 1000; x += 100) {
double normalized_x, normalized_y;
intrinsics.InvertIntrinsics(x, y, &normalized_x, &normalized_y);
double xp, yp;
intrinsics.ApplyIntrinsics(normalized_x, normalized_y, &xp, &yp);
EXPECT_NEAR(x, xp, 1e-8) << "y: " << y;
EXPECT_NEAR(y, yp, 1e-8) << "x: " << x;
LG << "Error x: " << (x - xp);
LG << "Error y: " << (y - yp);
}
}
}
TEST(PolynomialCameraIntrinsics, IdentityDistortBuffer) {
const int w = 101, h = 101;
FloatImage image(h, w);
image.Fill(0);
DrawLine(0.0, h / 2.0, w - 1, h / 2.0, 1.0, &image);
DrawLine(0.0, h / 4.0, w - 1, h / 4.0, 1.0, &image);
DrawLine(0.0, h / 4.0 * 3.0, w - 1.0, h / 4.0 * 3.0, 1.0, &image);
DrawLine(w / 2.0, 0.0, w / 2.0, h - 1.0, 1.0, &image);
DrawLine(w / 4.0, 0.0, w / 4.0, h - 1.0, 1.0, &image);
DrawLine(w / 4.0 * 3.0, 0.0, w / 4.0 * 3.0, h - 1.0, 1.0, &image);
PolynomialCameraIntrinsics intrinsics;
FloatImage distorted_image(h, w);
intrinsics.SetImageSize(w, h);
intrinsics.SetFocalLength(10.0, 10.0);
intrinsics.SetPrincipalPoint((double) w / 2.0, (double) h / 2.0);
intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
intrinsics.DistortBuffer(image.Data(),
image.Width(), image.Height(),
0.0,
image.Depth(),
distorted_image.Data());
for (int x = 0; x < image.Width(); ++x) {
for (int y = 0; y < image.Height(); ++y) {
EXPECT_EQ(image(y, x), distorted_image(y, x));
}
}
}
TEST(PolynomialCameraIntrinsics, IdentityUndistortBuffer) {
const int w = 101, h = 101;
FloatImage image(h, w);
image.Fill(0);
DrawLine(0.0, h / 2.0, w - 1, h / 2.0, 1.0, &image);
DrawLine(0.0, h / 4.0, w - 1, h / 4.0, 1.0, &image);
DrawLine(0.0, h / 4.0 * 3.0, w - 1.0, h / 4.0 * 3.0, 1.0, &image);
DrawLine(w / 2.0, 0.0, w / 2.0, h - 1.0, 1.0, &image);
DrawLine(w / 4.0, 0.0, w / 4.0, h - 1.0, 1.0, &image);
DrawLine(w / 4.0 * 3.0, 0.0, w / 4.0 * 3.0, h - 1.0, 1.0, &image);
PolynomialCameraIntrinsics intrinsics;
FloatImage distorted_image(h, w);
intrinsics.SetImageSize(w, h);
intrinsics.SetFocalLength(10.0, 10.0);
intrinsics.SetPrincipalPoint((double) w / 2.0, (double) h / 2.0);
intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
intrinsics.UndistortBuffer(image.Data(),
image.Width(), image.Height(),
0.0,
image.Depth(),
distorted_image.Data());
for (int x = 0; x < image.Width(); ++x) {
for (int y = 0; y < image.Height(); ++y) {
EXPECT_EQ(image(y, x), distorted_image(y, x));
}
}
}
} // namespace libmv

View File

@ -0,0 +1,230 @@
// Copyright (c) 2014 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/simple_pipeline/detect.h"
#include "testing/testing.h"
#include "libmv/logging/logging.h"
namespace libmv {
namespace {
void PreformSinglePointTest(const DetectOptions &options) {
// Prepare the image.
FloatImage image(15, 15);
image.fill(1.0);
image(7, 7) = 0.0;
// Run the detector.
vector<Feature> detected_features;
Detect(image, options, &detected_features);
// Check detected features matches our expectations.
EXPECT_EQ(1, detected_features.size());
if (detected_features.size() == 1) {
Feature &feature = detected_features[0];
EXPECT_EQ(7, feature.x);
EXPECT_EQ(7, feature.y);
}
}
void PreformCheckerBoardTest(const DetectOptions &options) {
// Prepare the image.
FloatImage image(30, 30);
for (int y = 0; y < image.Height(); ++y) {
for (int x = 0; x < image.Width(); ++x) {
image(y, x) = (x / 10 + y / 10) % 2 ? 1.0 : 0.0;
}
}
// Run the detector.
vector<Feature> detected_features;
Detect(image, options, &detected_features);
// Check detected features matches our expectations.
// We expect here only corners of a center square to be
// considered a feature points.
EXPECT_EQ(4, detected_features.size());
// We don't know which side of the corner detector will choose,
// so what we're checking here is that detected feature is from
// any side of the corner.
//
// This doesn't check whether there're multiple features which
// are placed on different sides of the same corner. The way we
// deal with this is requiring min_distance to be greater than 2px.
for (int i = 0; i < detected_features.size(); ++i) {
Feature &feature = detected_features[i];
int rounded_x = ((feature.x + 1) / 10) * 10,
rounded_y = ((feature.y + 1) / 10) * 10;
EXPECT_LE(1, std::abs(feature.x - rounded_x));
EXPECT_LE(1, std::abs(feature.y - rounded_y));
}
}
void CheckExpectedFeatures(const vector<Feature> &detected_features,
const vector<Feature> &expected_features) {
EXPECT_EQ(expected_features.size(), detected_features.size());
// That's unsafe to iterate over vectors when their lengths
// doesn't match. And it doesn't make any sense actually since
// the test will already be considered failed here.
if (expected_features.size() != detected_features.size()) {
return;
}
for (int i = 0; i < expected_features.size(); ++i) {
const Feature &extected_feature = expected_features[i];
bool found = false;
for (int j = 0; j < detected_features.size(); ++j) {
const Feature &detected_feature = detected_features[j];
if (extected_feature.x == detected_feature.x &&
extected_feature.y == detected_feature.y) {
found = true;
break;
}
}
EXPECT_TRUE(found);
}
}
void PreformSingleTriangleTest(const DetectOptions &options) {
// Prepare the image.
FloatImage image(15, 21);
image.fill(1.0);
int vertex_x = 10, vertex_y = 5;
for (int i = 0; i < 6; ++i) {
int current_x = vertex_x - i,
current_y = vertex_y + i;
for (int j = 0; j < i * 2 + 1; ++j, ++current_x) {
image(current_y, current_x) = 0.0;
}
}
// Run the detector.
vector<Feature> detected_features;
Detect(image, options, &detected_features);
// Check detected features matches our expectations.
vector<Feature> expected_features;
expected_features.push_back(Feature(6, 10));
expected_features.push_back(Feature(14, 10));
expected_features.push_back(Feature(10, 6));
CheckExpectedFeatures(detected_features, expected_features);
}
} // namespace
#ifndef LIBMV_NO_FAST_DETECTOR
TEST(Detect, FASTSinglePointTest) {
DetectOptions options;
options.type = DetectOptions::FAST;
options.min_distance = 0;
options.fast_min_trackness = 1;
PreformSinglePointTest(options);
}
#endif // LIBMV_NO_FAST_DETECTOR
#if 0
// TODO(sergey): FAST doesn't detect checker board corners, but should it?
TEST(Detect, FASTCheckerBoardTest) {
DetectOptions options;
options.type = DetectOptions::FAST;
options.min_distance = 0;
options.fast_min_trackness = 1;
PreformCheckerBoardTest(options);
}
#endif
#if 0
// TODO(sergey): FAST doesn't detect triangle corners!
TEST(Detect, FASTSingleTriangleTest) {
DetectOptions options;
options.type = DetectOptions::FAST;
options.margin = 3;
options.min_distance = 0;
options.fast_min_trackness = 2;
PreformSingleTriangleTest(options);
}
#endif
#if 0
// TODO(sergey): This doesn't actually detect single point,
// but should it or it's expected that Moravec wouldn't consider
// single point as feature?
//
// Uncomment this or remove as soon as we know answer for the
// question.
TEST(Detect, MoravecSinglePointTest) {
DetectOptions options;
options.type = DetectOptions::MORAVEC;
options.min_distance = 0;
options.moravec_max_count = 10;
PreformSinglePointTest(options);
}
// TODO(sergey): Moravec doesn't detect checker board corners, but should it?
TEST(Detect, MoravecCheckerBoardTest) {
DetectOptions options;
options.type = DetectOptions::MORAVEC;
options.min_distance = 0;
options.moravec_max_count = 10;
PreformCheckerBoardTest(options);
}
#endif
TEST(Detect, HarrisSinglePointTest) {
DetectOptions options;
options.type = DetectOptions::HARRIS;
// Set this to non-zero so image corners are not considered
// a feature points and avoid center point neighbors to be
// considered a features as well.
options.margin = 3;
options.min_distance = 3;
PreformSinglePointTest(options);
}
TEST(Detect, HarrisSingleTriangleTest) {
DetectOptions options;
options.type = DetectOptions::HARRIS;
options.margin = 3;
options.min_distance = 2;
options.harris_threshold = 1e-3;
PreformSingleTriangleTest(options);
}
// TODO(sergey): Add tests for margin option.
// TODO(sergey): Add tests for min_distance option.
} // namespace libmv

View File

@ -0,0 +1,81 @@
// Copyright (c) 2007, 2008 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/simple_pipeline/intersect.h"
#include <iostream>
#include "testing/testing.h"
#include "libmv/multiview/projection.h"
#include "libmv/numeric/numeric.h"
#include "libmv/logging/logging.h"
namespace libmv {
TEST(Intersect, EuclideanIntersect) {
Mat3 K1 = Mat3::Identity();
// K1 << 320, 0, 160,
// 0, 320, 120,
// 0, 0, 1;
Mat3 K2 = Mat3::Identity();
// K2 << 360, 0, 170,
// 0, 360, 110,
// 0, 0, 1;
Mat3 R1 = RotationAroundZ(-0.1);
Mat3 R2 = RotationAroundX(-0.1);
Vec3 t1; t1 << 1, 1, 10;
Vec3 t2; t2 << -2, -1, 10;
Mat34 P1, P2;
P_From_KRt(K1, R1, t1, &P1);
P_From_KRt(K2, R2, t2, &P2);
//Mat3 F; FundamentalFromProjections(P1, P2, &F);
Mat3X X;
X.resize(3, 30);
X.setRandom();
Mat2X X1, X2;
Project(P1, X, &X1);
Project(P2, X, &X2);
for (int i = 0; i < X.cols(); ++i) {
Vec2 x1, x2;
MatrixColumn(X1, i, &x1);
MatrixColumn(X2, i, &x2);
Vec3 expected;
MatrixColumn(X, i, &expected);
EuclideanReconstruction reconstruction;
reconstruction.InsertCamera(1, R1, t1);
reconstruction.InsertCamera(2, R2, t2);
vector<Marker> markers;
Marker a = { 1, 0, x1.x(), x1.y(), 1.0 };
markers.push_back(a);
Marker b = { 2, 0, x2.x(), x2.y(), 1.0 };
markers.push_back(b);
EuclideanIntersect(markers, &reconstruction);
Vec3 estimated = reconstruction.PointForTrack(0)->X;
EXPECT_NEAR(0, DistanceLInfinity(estimated, expected), 1e-8);
}
}
} // namespace

View File

@ -0,0 +1,307 @@
// Copyright (c) 2011 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/simple_pipeline/keyframe_selection.h"
#include "testing/testing.h"
#include "libmv/simple_pipeline/camera_intrinsics.h"
#include "libmv/logging/logging.h"
namespace libmv {
// Synthetic test, N markers with the same translation
// Should not be keyframe
TEST(KeyframeSelection, SyntheticNeighborFrame) {
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(900.0,900.0);
intrinsics.SetPrincipalPoint(640.0, 540.0);
intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
Tracks tracks;
const int markers_per_size = 15;
// Fill in tracks for homography estimation
for (int x = 0; x < markers_per_size; x++) {
for (int y = 0; y < markers_per_size; y++) {
double current_x = 10 + x * 40, current_y = 10 + y * 40;
double next_x = current_x + 10, next_y = current_y + 10;
intrinsics.InvertIntrinsics(current_x, current_y, &current_x, &current_y);
intrinsics.InvertIntrinsics(next_x, next_y, &next_x, &next_y);
tracks.Insert(1, y * markers_per_size + x, current_x, current_y);
tracks.Insert(2, y * markers_per_size + x, next_x, next_y);
}
}
vector<int> keyframes;
SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
// Synthetic second frame shouldn't be considered a keyframe
EXPECT_EQ(0, keyframes.size());
}
// Frames 1 and 2 of FabrikEingang footage
// Only one wall is tracked, should not be keyframes
TEST(KeyframeSelection, FabrikEingangNeighborFrames) {
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(1605.797, 1605.797);
intrinsics.SetPrincipalPoint(960.000, 544.000);
intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
Marker markers[] = {
{1, 0, 737.599983, 646.397594, 1.0}, {2, 0, 737.906628, 648.113327, 1.0}, {1, 1, 863.045425, 646.081905, 1.0},
{2, 1, 863.339767, 647.650040, 1.0}, {1, 2, 736.959972, 574.080151, 1.0}, {2, 2, 737.217350, 575.604900, 1.0},
{1, 3, 864.097424, 573.374908, 1.0}, {2, 3, 864.383469, 574.900307, 1.0}, {1, 4, 789.429073, 631.677521, 1.0},
{2, 4, 789.893131, 633.124451, 1.0}, {1, 5, 791.051960, 573.442028, 1.0}, {2, 5, 791.336575, 575.088890, 1.0},
{1, 6, 738.973961, 485.130308, 1.0}, {2, 6, 739.435501, 486.734207, 1.0}, {1, 7, 862.403240, 514.866074, 1.0},
{2, 7, 862.660618, 516.413261, 1.0}, {1, 8, 802.240162, 485.759838, 1.0}, {2, 8, 802.602253, 487.432899, 1.0},
{1, 9, 754.340630, 500.624559, 1.0}, {2, 9, 754.559956, 502.079920, 1.0}, {1, 10, 849.398689, 484.480545, 1.0},
{2, 10, 849.599934, 486.079937, 1.0}, {1, 11, 788.803768, 515.924391, 1.0}, {2, 11, 789.119911, 517.439932, 1.0},
{1, 12, 838.733940, 558.212688, 1.0}, {2, 12, 839.039898, 559.679916, 1.0}, {1, 13, 760.014782, 575.194466, 1.0},
{2, 13, 760.319881, 576.639904, 1.0}, {1, 14, 765.321636, 616.015957, 1.0}, {2, 14, 765.759945, 617.599915, 1.0},
{1, 15, 800.963230, 660.032082, 1.0}, {2, 15, 801.279945, 661.759876, 1.0}, {1, 16, 846.321087, 602.313053, 1.0},
{2, 16, 846.719913, 603.839878, 1.0}, {1, 17, 864.288311, 616.790524, 1.0}, {2, 17, 864.639931, 618.239918, 1.0},
{1, 18, 800.006790, 602.573425, 1.0}, {2, 18, 800.319958, 604.159912, 1.0}, {1, 19, 739.026890, 617.944138, 1.0},
{2, 19, 739.199924, 619.519924, 1.0}, {1, 20, 801.987419, 544.134888, 1.0}, {2, 20, 802.239933, 545.599911, 1.0},
{1, 21, 753.619823, 542.961300, 1.0}, {2, 21, 753.919945, 544.639874, 1.0}, {1, 22, 787.921257, 499.910206, 1.0},
{2, 22, 788.159924, 501.439917, 1.0}, {1, 23, 839.095459, 529.287903, 1.0}, {2, 23, 839.359932, 530.879934, 1.0},
{1, 24, 811.760330, 630.732269, 1.0}, {2, 24, 812.159901, 632.319859, 1.0}
};
int num_markers = sizeof(markers) / sizeof(Marker);
Tracks tracks;
for (int i = 0; i < num_markers; i++) {
double x = markers[i].x, y = markers[i].y;
intrinsics.InvertIntrinsics(x, y, &x, &y);
tracks.Insert(markers[i].image, markers[i].track, x, y);
}
vector<int> keyframes;
SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
EXPECT_EQ(0, keyframes.size());
}
// Frames 120 and 200 from FabrikEingang footage
// Should be enough of parallax for keyframing
TEST(KeyframeSelection, FabrikEingangFarFrames) {
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(1605.797, 1605.797);
intrinsics.SetPrincipalPoint(960.000, 544.000);
intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
Marker markers[] = {
{1, 0, 369.459200, 619.315258, 1.0}, {2, 0, 279.677496, 722.086842, 1.0}, {1, 1, 376.831970, 370.278397, 1.0},
{2, 1, 221.695247, 460.065418, 1.0}, {1, 2, 1209.139023, 567.705605, 1.0}, {2, 2, 1080.760117, 659.230083, 1.0},
{1, 3, 1643.495750, 903.620453, 1.0}, {2, 3, 1618.405037, 1015.374908, 1.0}, {1, 4, 1494.849815, 425.302460, 1.0},
{2, 4, 1457.467575, 514.727587, 1.0}, {1, 5, 1794.637299, 328.728609, 1.0}, {2, 5, 1742.161446, 408.988636, 1.0},
{1, 6, 1672.822723, 102.240358, 1.0}, {2, 6, 1539.287224, 153.536892, 1.0}, {1, 7, 1550.843925, 53.424943, 1.0},
{2, 7, 1385.579109, 96.450085, 1.0}, {1, 8, 852.953281, 465.399578, 1.0}, {2, 8, 779.404564, 560.091843, 1.0},
{1, 9, 906.853752, 299.827040, 1.0}, {2, 9, 786.923218, 385.570770, 1.0}, {1, 10, 406.322966, 87.556041, 1.0},
{2, 10, 140.339413, 150.877481, 1.0}, {1, 11, 254.811573, 851.296478, 1.0}, {2, 11, 94.478302, 969.350189, 1.0},
{1, 12, 729.087868, 806.092758, 1.0}, {2, 12, 606.212139, 919.876560, 1.0}, {1, 13, 1525.719452, 920.398083, 1.0},
{2, 13, 1495.579720, 1031.971218, 1.0}
};
int num_markers = sizeof(markers) / sizeof(Marker);
Tracks tracks;
for (int i = 0; i < num_markers; i++) {
double x = markers[i].x, y = markers[i].y;
intrinsics.InvertIntrinsics(x, y, &x, &y);
tracks.Insert(markers[i].image, markers[i].track, x, y);
}
vector<int> keyframes;
SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
EXPECT_EQ(2, keyframes.size());
}
// Manually selected keyframes from copter footage from Sebastian
// Keyframes were 167 and 237
TEST(KeyframeSelection, CopterManualKeyFrames) {
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(1155.043, 1155.043);
intrinsics.SetPrincipalPoint(640.000, 360.000);
intrinsics.SetRadialDistortion(-0.08590, 0.0, 0.0);
Marker markers[] = {
{1, 0, 645.792694, 403.115931, 1.0}, {2, 0, 630.641174, 307.996409, 1.0}, {1, 1, 783.469086, 403.904328, 1.0},
{2, 1, 766.001129, 308.998225, 1.0}, {1, 2, 650.000000, 160.000001, 1.0}, {1, 3, 785.225906, 158.619039, 1.0},
{2, 3, 767.526474, 70.449695, 1.0}, {1, 4, 290.640526, 382.335634, 1.0}, {2, 4, 273.001728, 86.993319, 1.0},
{1, 5, 291.162739, 410.602684, 1.0}, {2, 5, 273.287849, 111.937487, 1.0}, {1, 6, 136.919317, 349.895797, 1.0},
{1, 7, 490.844345, 47.572222, 1.0}, {1, 8, 454.406433, 488.935761, 1.0}, {1, 9, 378.655815, 618.522248, 1.0},
{2, 9, 357.061806, 372.265077, 1.0}, {1, 10, 496.011391, 372.668824, 1.0}, {2, 10, 477.979164, 222.986112, 1.0},
{1, 11, 680.060272, 256.103625, 1.0}, {2, 11, 670.587540, 204.830453, 1.0}, {1, 12, 1070.817108, 218.775322, 1.0},
{2, 12, 1046.129913, 128.969783, 1.0}, {1, 14, 242.516403, 596.048512, 1.0}, {2, 14, 224.182606, 248.272154, 1.0},
{1, 15, 613.936272, 287.519073, 1.0}, {2, 15, 600.467644, 196.085722, 1.0}, {1, 31, 844.637451, 256.354315, 1.0},
{2, 31, 823.200150, 165.714952, 1.0},
};
int num_markers = sizeof(markers) / sizeof(Marker);
Tracks tracks;
for (int i = 0; i < num_markers; i++) {
double x = markers[i].x, y = markers[i].y;
intrinsics.InvertIntrinsics(x, y, &x, &y);
tracks.Insert(markers[i].image, markers[i].track, x, y);
}
vector<int> keyframes;
SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
EXPECT_EQ(2, keyframes.size());
}
// Used old friend elevator scene MMI_2366 with automatic feature selection
// and manual outlier elimination and manual keyframe selection
// Selected keyframes were 29 and 41
TEST(KeyframeSelection, ElevatorManualKeyframesFrames) {
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(1380.000, 1380.000);
intrinsics.SetPrincipalPoint(960.000, 540.000);
intrinsics.SetRadialDistortion(-0.034, 0.0, 0.0);
Marker markers[] = {
{1, 2, 1139.861412, 1034.634984, 1.0}, {2, 2, 1143.512192, 1065.355718, 1.0}, {1, 3, 1760.821953, 644.658036, 1.0},
{2, 3, 1770.901108, 697.899928, 1.0}, {1, 4, 858.071823, 1068.520746, 1.0}, {1, 6, 1633.952408, 797.050145, 1.0},
{2, 6, 1642.508469, 849.157140, 1.0}, {1, 8, 1716.695824, 451.805491, 1.0}, {2, 8, 1726.513939, 502.095687, 1.0},
{1, 9, 269.577627, 724.986935, 1.0}, {2, 9, 269.424820, 764.154246, 1.0}, {1, 10, 1891.321907, 706.948843, 1.0},
{2, 10, 1903.338547, 766.068377, 1.0}, {1, 12, 1806.227074, 956.089604, 1.0}, {2, 12, 1816.619568, 1013.767376, 1.0},
{1, 14, 269.544153, 1002.333570, 1.0}, {2, 14, 269.367542, 1043.509254, 1.0}, {1, 15, 1402.772141, 281.392962, 1.0},
{2, 15, 1409.089165, 318.731629, 1.0}, {1, 16, 195.877233, 919.454341, 1.0}, {2, 16, 192.531109, 997.367899, 1.0},
{1, 17, 1789.584045, 120.036661, 1.0}, {2, 17, 1800.391846, 167.822964, 1.0}, {1, 18, 999.363213, 765.004807, 1.0},
{2, 18, 1002.345772, 790.560122, 1.0}, {1, 19, 647.342491, 1044.805727, 1.0}, {2, 19, 649.328041, 1058.682940, 1.0},
{1, 20, 1365.486832, 440.901829, 1.0}, {2, 20, 1371.413040, 477.888730, 1.0}, {1, 21, 1787.125282, 301.431606, 1.0},
{2, 21, 1798.527260, 355.224531, 1.0}, {1, 22, 1257.805824, 932.797258, 1.0}, {2, 22, 1263.017578, 969.376774, 1.0},
{1, 23, 961.969528, 843.148112, 1.0}, {2, 23, 964.869461, 868.587620, 1.0}, {1, 24, 158.076110, 1052.643592, 1.0},
{1, 25, 1072.884521, 1005.296981, 1.0}, {2, 25, 1076.091156, 1032.776856, 1.0}, {1, 26, 1107.656937, 526.577228, 1.0},
{2, 26, 1111.618423, 555.524454, 1.0}, {1, 27, 1416.410751, 529.857581, 1.0}, {2, 27, 1422.663574, 570.025957, 1.0},
{1, 28, 1498.673630, 1005.453086, 1.0}, {2, 28, 1505.381813, 1051.827149, 1.0}, {1, 29, 1428.647804, 652.473629, 1.0},
{2, 29, 1434.898224, 692.715390, 1.0}, {1, 30, 1332.318764, 503.673599, 1.0}, {2, 30, 1338.000069, 540.507967, 1.0},
{1, 32, 1358.642693, 709.837904, 1.0}, {2, 32, 1364.231529, 748.678265, 1.0}, {1, 33, 1850.911560, 460.475668, 1.0},
{2, 33, 1862.221413, 512.797347, 1.0}, {1, 34, 1226.117821, 607.053959, 1.0}, {2, 34, 1230.736084, 641.091449, 1.0},
{1, 35, 619.598236, 523.341744, 1.0}, {2, 35, 621.601124, 554.453287, 1.0}, {1, 36, 956.591492, 958.223183, 1.0},
{2, 36, 959.289265, 983.289263, 1.0}, {1, 37, 1249.922218, 419.095856, 1.0}, {2, 37, 1255.005913, 452.556177, 1.0},
{1, 39, 1300.528450, 386.251166, 1.0}, {2, 39, 1305.957413, 420.185595, 1.0}, {1, 40, 1128.689919, 972.558346, 1.0},
{2, 40, 1132.413712, 1003.984737, 1.0}, {1, 41, 503.304749, 1053.504388, 1.0}, {2, 41, 505.019703, 1069.175613, 1.0},
{1, 42, 1197.352982, 472.681564, 1.0}, {2, 42, 1201.910706, 503.459880, 1.0}, {1, 43, 1794.391022, 383.911400, 1.0},
{2, 43, 1805.324135, 436.116468, 1.0}, {1, 44, 789.641418, 1058.045647, 1.0}, {1, 45, 1376.575241, 928.714979, 1.0},
{2, 45, 1381.995850, 969.511957, 1.0}, {1, 46, 1598.023567, 93.975592, 1.0}, {2, 46, 1606.937141, 136.827035, 1.0},
{1, 47, 1455.550232, 762.128685, 1.0}, {2, 47, 1462.014313, 805.164878, 1.0}, {1, 48, 1357.123489, 354.460326, 1.0},
{2, 48, 1363.071899, 390.363121, 1.0}, {1, 49, 939.792652, 781.549895, 1.0}, {2, 49, 942.802620, 806.164012, 1.0},
{1, 50, 1380.251083, 805.948620, 1.0}, {2, 50, 1385.637932, 845.592098, 1.0}, {1, 51, 1021.769943, 1049.802361, 1.0},
{1, 52, 1065.634918, 608.099055, 1.0}, {2, 52, 1069.142189, 635.361736, 1.0}, {1, 53, 624.324188, 463.202863, 1.0},
{2, 53, 626.395454, 494.994088, 1.0}, {1, 54, 1451.459885, 881.557624, 1.0}, {2, 54, 1457.679634, 924.345531, 1.0},
{1, 55, 1201.885986, 1057.079022, 1.0}, {1, 56, 581.157532, 947.661438, 1.0}, {2, 56, 583.242359, 960.831449, 1.0},
{1, 58, 513.593102, 954.175858, 1.0}, {2, 58, 515.470047, 971.309574, 1.0}, {1, 59, 928.069038, 901.774421, 1.0},
{2, 59, 930.847950, 925.613744, 1.0}, {1, 60, 1065.860023, 740.395389, 1.0}, {2, 60, 1069.484253, 768.971086, 1.0},
{1, 61, 990.479393, 906.264632, 1.0}, {2, 61, 993.217506, 933.088803, 1.0}, {1, 62, 1776.196747, 776.278453, 1.0},
{2, 62, 1786.292496, 831.136880, 1.0}, {1, 63, 834.454365, 1012.449725, 1.0}, {2, 63, 836.868324, 1033.451807, 1.0},
{1, 64, 1355.190697, 869.184809, 1.0}, {2, 64, 1360.736618, 909.773347, 1.0}, {1, 65, 702.072487, 897.519686, 1.0},
{2, 65, 704.203377, 911.931131, 1.0}, {1, 66, 1214.022903, 856.199934, 1.0}, {2, 66, 1218.109016, 890.753052, 1.0},
{1, 67, 327.676048, 236.814036, 1.0}, {2, 67, 328.335285, 277.251878, 1.0}, {1, 68, 289.064083, 454.793912, 1.0},
{2, 68, 288.651924, 498.882444, 1.0}, {1, 69, 1626.240692, 278.374350, 1.0}, {2, 69, 1634.131508, 315.853672, 1.0},
{1, 70, 1245.375710, 734.862142, 1.0}, {2, 70, 1250.047417, 769.670885, 1.0}, {1, 71, 497.015305, 510.718904, 1.0},
{2, 71, 498.682308, 541.070201, 1.0}, {1, 72, 1280.542030, 153.939185, 1.0}, {2, 72, 1286.993637, 198.436196, 1.0},
{1, 73, 1534.748840, 138.601043, 1.0}, {2, 73, 1542.961349, 180.170819, 1.0}, {1, 74, 1477.412682, 200.608061, 1.0},
{2, 74, 1484.683914, 240.413260, 1.0}, {1, 76, 450.637321, 407.279642, 1.0}, {2, 76, 451.695642, 441.666291, 1.0},
{1, 78, 246.981239, 220.786298, 1.0}, {2, 78, 244.524879, 290.016564, 1.0}, {1, 79, 36.696489, 420.023407, 1.0},
{2, 79, 21.364746, 591.245492, 1.0},
};
int num_markers = sizeof(markers) / sizeof(Marker);
Tracks tracks;
for (int i = 0; i < num_markers; i++) {
double x = markers[i].x, y = markers[i].y;
intrinsics.InvertIntrinsics(x, y, &x, &y);
tracks.Insert(markers[i].image, markers[i].track, x, y);
}
vector<int> keyframes;
SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
EXPECT_EQ(2, keyframes.size());
}
// Elevator scene MMI_2366 with manual tracks, frames 1, 2, 3, 5 and 27
TEST(KeyframeSelection, ElevatorReconstructionVarianceTest) {
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(1380.000, 1380.000);
intrinsics.SetPrincipalPoint(960.000, 540.000);
intrinsics.SetRadialDistortion(-0.034, 0.0, 0.0);
Marker markers[] = {
{1, 0, 182.999997, 1047.000010, 1.0}, {2, 0, 181.475730, 1052.091079, 1.0}, {3, 0, 181.741562, 1057.893341, 1.0},
{4, 0, 183.190498, 1068.310440, 1.0}, {1, 1, 271.000013, 666.000009, 1.0}, {2, 1, 270.596180, 668.665760, 1.0},
{3, 1, 270.523510, 671.559069, 1.0}, {4, 1, 271.856518, 676.818151, 1.0}, {5, 1, 268.989000, 727.051570, 1.0},
{1, 2, 264.999990, 1018.000031, 1.0}, {2, 2, 264.020061, 1021.157591, 1.0}, {3, 2, 264.606056, 1024.823506, 1.0},
{4, 2, 266.200933, 1031.168690, 1.0}, {1, 3, 270.000000, 938.000014, 1.0}, {2, 3, 269.022617, 941.153390, 1.0},
{3, 3, 269.605579, 944.454954, 1.0}, {4, 3, 271.281366, 949.452167, 1.0}, {5, 3, 268.963480, 1004.417453, 1.0},
{1, 4, 200.999994, 799.000003, 1.0}, {2, 4, 199.841366, 803.891838, 1.0}, {3, 4, 200.262208, 809.323246, 1.0},
{4, 4, 201.456513, 819.271195, 1.0}, {5, 4, 195.026493, 924.363234, 1.0}, {1, 5, 1775.000038, 49.999998, 1.0},
{2, 5, 1775.255127, 53.718264, 1.0}, {3, 5, 1776.449890, 55.951670, 1.0}, {4, 5, 1778.815727, 61.923309, 1.0},
{5, 5, 1790.274124, 123.074923, 1.0}, {1, 6, 164.000001, 927.999988, 1.0}, {2, 6, 162.665462, 933.169527, 1.0},
{3, 6, 163.067923, 938.577182, 1.0}, {4, 6, 164.370360, 948.840945, 1.0}, {5, 6, 157.199407, 1057.762341, 1.0},
{1, 7, 618.000011, 477.999998, 1.0}, {2, 7, 617.583504, 480.124243, 1.0}, {3, 7, 618.356495, 482.441897, 1.0},
{4, 7, 619.792500, 486.428132, 1.0}, {5, 7, 619.546051, 525.222627, 1.0}, {1, 8, 499.999981, 1036.999984, 1.0},
{2, 8, 499.080162, 1038.720160, 1.0}, {3, 8, 499.949398, 1039.014344, 1.0}, {4, 8, 501.828003, 1041.286647, 1.0},
{5, 8, 502.777576, 1055.196369, 1.0}, {1, 9, 1587.000046, 31.999999, 1.0}, {2, 9, 1586.988373, 34.635853, 1.0},
{3, 9, 1588.155899, 37.444186, 1.0}, {4, 9, 1589.973106, 42.492081, 1.0}, {5, 9, 1598.683205, 96.526332, 1.0},
{1, 10, 622.999992, 416.999999, 1.0}, {2, 10, 622.449017, 419.233485, 1.0}, {3, 10, 623.283234, 421.500703, 1.0},
{4, 10, 624.620132, 425.537406, 1.0}, {5, 10, 624.290829, 465.078338, 1.0}, {1, 11, 577.999992, 931.999998, 1.0},
{2, 11, 577.042294, 932.872703, 1.0}, {3, 11, 577.832451, 934.045451, 1.0}, {4, 11, 579.729137, 935.735435, 1.0},
{5, 11, 580.691242, 948.396256, 1.0}, {1, 12, 510.999985, 931.999998, 1.0}, {2, 12, 510.111237, 933.152146, 1.0},
{3, 12, 510.797081, 934.454219, 1.0}, {4, 12, 512.647362, 936.595910, 1.0}, {5, 12, 513.247204, 955.144157, 1.0},
{1, 13, 330.459995, 177.059993, 1.0}, {2, 13, 329.876347, 179.615586, 1.0}, {3, 13, 330.681696, 182.757810, 1.0},
{4, 13, 331.345053, 187.903853, 1.0}, {5, 13, 327.824135, 239.611639, 1.0}, {1, 14, 291.813097, 388.516195, 1.0},
{2, 14, 290.984058, 391.382725, 1.0}, {3, 14, 291.526737, 394.778595, 1.0}, {4, 14, 292.763815, 400.310973, 1.0},
{5, 14, 288.714552, 457.548015, 1.0}, {1, 15, 496.491680, 466.534005, 1.0}, {2, 15, 495.909519, 468.518561, 1.0},
{3, 15, 496.588383, 470.853596, 1.0}, {4, 15, 497.976780, 474.731458, 1.0}, {5, 15, 496.998882, 512.568694, 1.0},
{1, 16, 1273.000031, 89.000000, 1.0}, {2, 16, 1272.951965, 92.003637, 1.0}, {3, 16, 1273.934784, 94.972191, 1.0},
{4, 16, 1275.493584, 100.139952, 1.0}, {5, 16, 1281.003571, 156.880163, 1.0}, {1, 17, 1524.713173, 78.852922, 1.0},
{2, 17, 1524.782066, 81.427142, 1.0}, {3, 17, 1525.759048, 84.057939, 1.0}, {4, 17, 1527.579689, 88.966550, 1.0},
{5, 17, 1535.262451, 141.186054, 1.0}, {1, 18, 1509.425011, 94.371824, 1.0}, {1, 19, 451.000013, 357.000003, 1.0},
{2, 19, 450.354881, 359.312410, 1.0}, {3, 19, 451.107473, 361.837088, 1.0}, {4, 19, 452.186537, 366.318061, 1.0},
{5, 19, 450.507660, 409.257599, 1.0}, {1, 20, 254.004936, 114.784185, 1.0}, {2, 20, 253.291512, 119.288486, 1.0},
{3, 20, 253.745584, 124.114957, 1.0}, {4, 20, 254.453287, 132.795120, 1.0}, {5, 20, 246.772242, 225.165337, 1.0},
{1, 21, 65.262880, 147.889409, 1.0}, {2, 21, 63.634465, 157.656807, 1.0}, {3, 21, 63.306799, 169.067053, 1.0},
{4, 21, 62.462311, 189.724241, 1.0}, {5, 21, 35.396615, 430.308380, 1.0},
};
int num_markers = sizeof(markers) / sizeof(Marker);
Tracks tracks;
for (int i = 0; i < num_markers; i++) {
double x = markers[i].x, y = markers[i].y;
intrinsics.InvertIntrinsics(x, y, &x, &y);
tracks.Insert(markers[i].image, markers[i].track, x, y);
}
vector<int> keyframes;
SelectKeyframesBasedOnGRICAndVariance(tracks, intrinsics, keyframes);
EXPECT_EQ(2, keyframes.size());
if (keyframes.size() == 2) {
EXPECT_EQ(1, keyframes[0]);
EXPECT_EQ(5, keyframes[1]);
}
}
} // namespace libmv

View File

@ -0,0 +1,79 @@
// Copyright (c) 2013 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/simple_pipeline/modal_solver.h"
#include "testing/testing.h"
#include "libmv/logging/logging.h"
#include "libmv/simple_pipeline/bundle.h"
#include "libmv/simple_pipeline/camera_intrinsics.h"
#include <stdio.h>
namespace libmv {
TEST(ModalSolver, SyntheticCubeSceneMotion) {
double kTolerance = 1e-8;
PolynomialCameraIntrinsics intrinsics;
intrinsics.SetFocalLength(658.286, 658.286);
intrinsics.SetPrincipalPoint(480.0, 270.0);
intrinsics.SetRadialDistortion(0.0, 0.0, 0.0);
Marker markers[] = {
{1, 0, 212.172775, 354.713538, 1.0}, {2, 0, 773.468399, 358.735306, 1.0},
{1, 1, 62.415197, 287.905354, 1.0}, {2, 1, 619.103336, 324.402537, 1.0},
{1, 2, 206.847939, 237.567925, 1.0}, {2, 2, 737.496986, 247.881383, 1.0},
{1, 3, 351.743889, 316.415906, 1.0}, {2, 3, 908.779621, 290.703617, 1.0},
{1, 4, 232.941413, 54.265443, 1.0}, {2, 4, 719.444847, 63.062531, 1.0},
{1, 5, 96.391611, 119.283537, 1.0}, {2, 5, 611.413136, 160.890715, 1.0},
{1, 6, 363.444958, 150.838144, 1.0}, {2, 6, 876.374531, 114.916206, 1.0},
};
int num_markers = sizeof(markers) / sizeof(Marker);
Tracks tracks;
for (int i = 0; i < num_markers; i++) {
double x = markers[i].x, y = markers[i].y;
intrinsics.InvertIntrinsics(x, y, &x, &y);
tracks.Insert(markers[i].image, markers[i].track, x, y);
}
EuclideanReconstruction reconstruction;
ModalSolver(tracks, &reconstruction);
EuclideanBundleCommonIntrinsics(tracks,
BUNDLE_NO_INTRINSICS,
BUNDLE_NO_TRANSLATION,
&reconstruction,
&intrinsics,
NULL);
Mat3 expected_rotation;
expected_rotation << 0.98215101299251, 0.17798357184544, 0.06083778292258,
-0.16875286001759, 0.97665299913606, -0.13293378620359,
-0.08307743323957, 0.12029450291547, 0.98925596922871;
Mat3 &first_camera_R = reconstruction.CameraForImage(1)->R;
Mat3 &second_camera_R = reconstruction.CameraForImage(2)->R;
EXPECT_TRUE(Mat3::Identity().isApprox(first_camera_R, kTolerance));
EXPECT_TRUE(expected_rotation.isApprox(second_camera_R, kTolerance));
}
} // namespace libmv

View File

@ -0,0 +1,234 @@
// Copyright (c) 2009 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/simple_pipeline/resect.h"
#include "libmv/logging/logging.h"
#include "testing/testing.h"
#if 0
// Generates all necessary inputs and expected outputs for EuclideanResection.
void CreateCameraSystem(const Mat3& KK,
const Mat3X& x_image,
const Vec& X_distances,
const Mat3& R_input,
const Vec3& T_input,
Mat2X *x_camera,
Mat3X *X_world,
Mat3 *R_expected,
Vec3 *T_expected) {
int num_points = x_image.cols();
Mat3X x_unit_cam(3, num_points);
x_unit_cam = KK.inverse() * x_image;
// Create normalized camera coordinates to be used as an input to the PnP
// function, instead of using NormalizeColumnVectors(&x_unit_cam).
*x_camera = x_unit_cam.block(0, 0, 2, num_points);
for (int i = 0; i < num_points; ++i) {
x_unit_cam.col(i).normalize();
}
// Create the 3D points in the camera system.
Mat X_camera(3, num_points);
for (int i = 0; i < num_points; ++i) {
X_camera.col(i) = X_distances(i) * x_unit_cam.col(i);
}
// Apply the transformation to the camera 3D points
Mat translation_matrix(3, num_points);
translation_matrix.row(0).setConstant(T_input(0));
translation_matrix.row(1).setConstant(T_input(1));
translation_matrix.row(2).setConstant(T_input(2));
*X_world = R_input * X_camera + translation_matrix;
// Create the expected result for comparison.
*R_expected = R_input.transpose();
*T_expected = *R_expected * (-T_input);
};
TEST(AbsoluteOrientation, QuaternionSolution) {
int num_points = 4;
Mat X;
Mat Xp;
X = 100 * Mat::Random(3, num_points);
// Create a random translation and rotation.
Mat3 R_input;
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
Vec3 t_input;
t_input.setRandom();
t_input = 100 * t_input;
Mat translation_matrix(3, num_points);
translation_matrix.row(0).setConstant(t_input(0));
translation_matrix.row(1).setConstant(t_input(1));
translation_matrix.row(2).setConstant(t_input(2));
// Create the transformed 3D points Xp as Xp = R * X + t.
Xp = R_input * X + translation_matrix;
// Output variables.
Mat3 R;
Vec3 t;
AbsoluteOrientation(X, Xp, &R, &t);
EXPECT_MATRIX_NEAR(t, t_input, 1e-6);
EXPECT_MATRIX_NEAR(R, R_input, 1e-8);
}
TEST(EuclideanResection, Points4KnownImagePointsRandomTranslationRotation) {
// In this test only the translation and rotation are random. The image
// points are selected from a real case and are well conditioned.
Vec2i image_dimensions;
image_dimensions << 1600, 1200;
Mat3 KK;
KK << 2796, 0, 804,
0 , 2796, 641,
0, 0, 1;
// The real image points.
int num_points = 4;
Mat3X x_image(3, num_points);
x_image << 1164.06, 734.948, 749.599, 430.727,
681.386, 844.59, 496.315, 580.775,
1, 1, 1, 1;
// A vector of the 4 distances to the 3D points.
Vec X_distances = 100 * Vec::Random(num_points).array().abs();
// Create the random camera motion R and t that resection should recover.
Mat3 R_input;
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
Vec3 T_input;
T_input.setRandom();
T_input = 100 * T_input;
// Create the camera system, also getting the expected result of the
// transformation.
Mat3 R_expected;
Vec3 T_expected;
Mat3X X_world;
Mat2X x_camera;
CreateCameraSystem(KK, x_image, X_distances, R_input, T_input,
&x_camera, &X_world, &R_expected, &T_expected);
// Finally, run the code under test.
Mat3 R_output;
Vec3 T_output;
EuclideanResection(x_camera, X_world,
&R_output, &T_output,
RESECTION_ANSAR_DANIILIDIS);
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
// For now, the EPnP doesn't have a non-linear optimization step and so is
// not precise enough with only 4 points.
//
// TODO(jmichot): Reenable this test when there is nonlinear refinement.
#if 0
R_output.setIdentity();
T_output.setZero();
EuclideanResection(x_camera, X_world,
&R_output, &T_output,
RESECTION_EPNP);
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);*/
#endif
}
// TODO(jmichot): Reduce the code duplication here with the code above.
TEST(EuclideanResection, Points6AllRandomInput) {
Mat3 KK;
KK << 2796, 0, 804,
0 , 2796, 641,
0, 0, 1;
// Create random image points for a 1600x1200 image.
int w = 1600;
int h = 1200;
int num_points = 6;
Mat3X x_image(3, num_points);
x_image.row(0) = w * Vec::Random(num_points).array().abs();
x_image.row(1) = h * Vec::Random(num_points).array().abs();
x_image.row(2).setOnes();
// Normalized camera coordinates to be used as an input to the PnP function.
Mat2X x_camera;
Vec X_distances = 100 * Vec::Random(num_points).array().abs();
// Create the random camera motion R and t that resection should recover.
Mat3 R_input;
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
Vec3 T_input;
T_input.setRandom();
T_input = 100 * T_input;
// Create the camera system.
Mat3 R_expected;
Vec3 T_expected;
Mat3X X_world;
CreateCameraSystem(KK, x_image, X_distances, R_input, T_input,
&x_camera, &X_world, &R_expected, &T_expected);
// Test each of the resection methods.
{
Mat3 R_output;
Vec3 T_output;
EuclideanResection(x_camera, X_world,
&R_output, &T_output,
RESECTION_ANSAR_DANIILIDIS);
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
}
{
Mat3 R_output;
Vec3 T_output;
EuclideanResection(x_camera, X_world,
&R_output, &T_output,
RESECTION_EPNP);
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
}
{
Mat3 R_output;
Vec3 T_output;
EuclideanResection(x_image, X_world, KK,
&R_output, &T_output);
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
}
}
#endif

View File

@ -0,0 +1,51 @@
// Copyright (c) 2011 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/tracking/brute_region_tracker.h"
#include "libmv/image/image.h"
#include "libmv/logging/logging.h"
#include "testing/testing.h"
namespace libmv {
namespace {
TEST(KltRegionTracker, Track) {
Array3Df image1(51, 51);
image1.Fill(0);
Array3Df image2(image1);
int x0 = 25, y0 = 25;
int dx = 3, dy = 2;
image1(y0, x0) = 1.0f;
image2(y0 + dy, x0 + dx) = 1.0;
double x1 = x0;
double y1 = y0;
BruteRegionTracker tracker;
EXPECT_TRUE(tracker.Track(image1, image2, x0, y0, &x1, &y1));
EXPECT_NEAR(x1, x0 + dx, 0.001);
EXPECT_NEAR(y1, y0 + dy, 0.001);
}
} // namespace
} // namespace libmv

View File

@ -0,0 +1,51 @@
// Copyright (c) 2011 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/tracking/klt_region_tracker.h"
#include "libmv/image/image.h"
#include "testing/testing.h"
namespace libmv {
namespace {
TEST(KltRegionTracker, Track) {
Array3Df image1(51, 51);
image1.Fill(0);
Array3Df image2(image1);
int x0 = 25, y0 = 25;
int dx = 3, dy = 2;
image1(y0, x0) = 1.0f;
image2(y0 + dy, x0 + dx) = 1.0f;
double x1 = x0;
double y1 = y0;
KltRegionTracker tracker;
tracker.half_window_size = 6;
EXPECT_TRUE(tracker.Track(image1, image2, x0, y0, &x1, &y1));
EXPECT_NEAR(x1, x0 + dx, 0.001);
EXPECT_NEAR(y1, y0 + dy, 0.001);
}
} // namespace
} // namespace libmv

View File

@ -0,0 +1,80 @@
// Copyright (c) 2011 libmv authors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
#include "libmv/tracking/pyramid_region_tracker.h"
#include "libmv/tracking/klt_region_tracker.h"
#include "libmv/image/image.h"
#include "testing/testing.h"
namespace libmv {
namespace {
TEST(PyramidKltRegionTracker, Track) {
Array3Df image1(100, 100);
image1.Fill(0);
Array3Df image2(image1);
int x1 = 25, y1 = 25;
image1(y1 + 0, x1 + 0) = 1.0f;
image1(y1 + 0, x1 + 1) = 1.0f;
image1(y1 + 1, x1 + 0) = 1.0f;
image1(y1 + 1, x1 + 1) = 1.0f;
// Make the displacement too large for a single-level KLT.
int x2 = x1 + 6, y2 = y1 + 5;
image2(y2 + 0, x2 + 0) = 1.0f;
image2(y2 + 0, x2 + 1) = 1.0f;
image2(y2 + 1, x2 + 0) = 1.0f;
image2(y2 + 1, x2 + 1) = 1.0f;
// Use a small 5x5 tracking region.
int half_window_size = 3;
// Ensure that the track doesn't work with one level of KLT.
{
double x2_actual = x1;
double y2_actual = y1;
KltRegionTracker tracker;
tracker.half_window_size = half_window_size;
EXPECT_FALSE(tracker.Track(image1, image2, x1, y1,
&x2_actual, &y2_actual));
}
// Verify that it works with the pyramid tracker.
{
double x2_actual = x1;
double y2_actual = y1;
KltRegionTracker *klt_tracker = new KltRegionTracker;
klt_tracker->half_window_size = half_window_size;
PyramidRegionTracker tracker(klt_tracker, 3);
EXPECT_TRUE(tracker.Track(image1, image2, x1, y1,
&x2_actual, &y2_actual));
EXPECT_NEAR(x2_actual, x2, 0.001);
EXPECT_NEAR(y2_actual, y2, 0.001);
}
}
} // namespace
} // namespace libmv

View File

@ -154,6 +154,7 @@ endif()
extern_rangetree
extern_wcwidth
extern_libmv
extern_glog
)
if(WITH_MOD_CLOTH_ELTOPO)

View File

@ -174,6 +174,9 @@ if(WITH_BUILDINFO)
list(APPEND SRC
buildinfo.c
)
# make an object library so can load with it in tests
add_library(buildinofobj OBJECT buildinfo.c)
endif()
# message(STATUS "Configuring blender")
@ -884,241 +887,8 @@ unset(BLENDER_TEXT_FILES)
add_dependencies(blender makesdna)
get_property(BLENDER_LINK_LIBS GLOBAL PROPERTY BLENDER_LINK_LIBS)
setup_blender_sorted_libs()
list(APPEND BLENDER_LINK_LIBS
bf_windowmanager
bf_render
)
if(WITH_MOD_FLUID)
list(APPEND BLENDER_LINK_LIBS bf_intern_elbeem)
endif()
if(WITH_CYCLES)
list(APPEND BLENDER_LINK_LIBS
cycles_render
cycles_bvh
cycles_device
cycles_kernel
cycles_util
cycles_subd)
if(WITH_CYCLES_OSL)
list(APPEND BLENDER_LINK_LIBS cycles_kernel_osl)
endif()
endif()
#if(UNIX)
# Sort libraries
set(BLENDER_SORTED_LIBS
bf_windowmanager
bf_editor_space_api
bf_editor_space_action
bf_editor_space_buttons
bf_editor_space_console
bf_editor_space_file
bf_editor_space_graph
bf_editor_space_image
bf_editor_space_info
bf_editor_space_logic
bf_editor_space_nla
bf_editor_space_node
bf_editor_space_outliner
bf_editor_space_script
bf_editor_space_sequencer
bf_editor_space_text
bf_editor_space_time
bf_editor_space_userpref
bf_editor_space_view3d
bf_editor_space_clip
bf_editor_transform
bf_editor_util
bf_editor_uvedit
bf_editor_curve
bf_editor_gpencil
bf_editor_interface
bf_editor_mesh
bf_editor_metaball
bf_editor_object
bf_editor_armature
bf_editor_physics
bf_editor_render
bf_editor_screen
bf_editor_sculpt_paint
bf_editor_sound
bf_editor_animation
bf_editor_datafiles
bf_editor_mask
bf_editor_io
bf_render
bf_python
bf_python_ext
bf_python_mathutils
bf_python_bmesh
bf_freestyle
bf_ikplugin
bf_modifiers
bf_bmesh
bf_blenkernel
bf_nodes
bf_gpu
bf_blenloader
bf_imbuf
bf_blenlib
bf_intern_ghost
bf_intern_string
bf_avi
bf_imbuf_cineon
bf_imbuf_openexr
bf_imbuf_openimageio
bf_imbuf_dds
bf_collada
bf_intern_elbeem
bf_intern_memutil
bf_intern_guardedalloc
bf_intern_ctr
bf_intern_utfconv
ge_blen_routines
ge_converter
ge_phys_dummy
ge_phys_bullet
bf_intern_smoke
extern_minilzo
extern_lzma
extern_colamd
ge_logic_ketsji
extern_recastnavigation
ge_logic
ge_rasterizer
ge_oglrasterizer
ge_logic_expressions
ge_scenegraph
ge_logic_network
ge_logic_ngnetwork
ge_logic_loopbacknetwork
bf_intern_moto
extern_openjpeg
extern_redcode
ge_videotex
bf_rna
bf_dna
bf_blenfont
bf_intern_audaspace
bf_intern_mikktspace
bf_intern_dualcon
bf_intern_cycles
cycles_render
cycles_bvh
cycles_device
cycles_kernel
cycles_util
cycles_subd
bf_intern_raskter
bf_intern_opencolorio
extern_rangetree
extern_wcwidth
extern_libmv
)
if(WITH_COMPOSITOR)
# added for opencl compositor
list_insert_before(BLENDER_SORTED_LIBS "bf_blenkernel" "bf_compositor")
list_insert_after(BLENDER_SORTED_LIBS "bf_compositor" "bf_intern_opencl")
endif()
if(WITH_LIBMV)
list(APPEND BLENDER_SORTED_LIBS extern_ceres)
endif()
if(WITH_MOD_CLOTH_ELTOPO)
list(APPEND BLENDER_SORTED_LIBS extern_eltopo)
endif()
if(NOT WITH_SYSTEM_GLEW)
list(APPEND BLENDER_SORTED_LIBS extern_glew)
endif()
if(WITH_BINRELOC)
list(APPEND BLENDER_SORTED_LIBS extern_binreloc)
endif()
if(WITH_CXX_GUARDEDALLOC)
list(APPEND BLENDER_SORTED_LIBS bf_intern_guardedalloc_cpp)
endif()
if(WITH_IK_SOLVER)
list_insert_after(BLENDER_SORTED_LIBS "bf_intern_elbeem" "bf_intern_iksolver")
endif()
if(WITH_IK_ITASC)
list(APPEND BLENDER_SORTED_LIBS bf_intern_itasc)
endif()
if(WITH_CODEC_QUICKTIME)
list(APPEND BLENDER_SORTED_LIBS bf_quicktime)
endif()
if(WITH_INPUT_NDOF)
list(APPEND BLENDER_SORTED_LIBS bf_intern_ghostndof3dconnexion)
endif()
if(WITH_MOD_BOOLEAN)
list(APPEND BLENDER_SORTED_LIBS extern_carve)
endif()
if(WITH_GHOST_XDND)
list(APPEND BLENDER_SORTED_LIBS extern_xdnd)
endif()
if(WITH_CYCLES_OSL)
list_insert_after(BLENDER_SORTED_LIBS "cycles_kernel" "cycles_kernel_osl")
endif()
if(WITH_INTERNATIONAL)
list(APPEND BLENDER_SORTED_LIBS bf_intern_locale)
endif()
if(WITH_OPENNL)
list_insert_after(BLENDER_SORTED_LIBS "bf_render" "bf_intern_opennl")
endif()
if(WITH_BULLET)
list_insert_after(BLENDER_SORTED_LIBS "bf_blenkernel" "bf_intern_rigidbody")
endif()
if(WITH_BULLET AND NOT WITH_SYSTEM_BULLET)
list_insert_after(BLENDER_SORTED_LIBS "ge_logic_ngnetwork" "extern_bullet")
endif()
foreach(SORTLIB ${BLENDER_SORTED_LIBS})
set(REMLIB ${SORTLIB})
foreach(SEARCHLIB ${BLENDER_LINK_LIBS})
if(${SEARCHLIB} STREQUAL ${SORTLIB})
set(REMLIB "")
endif()
endforeach()
if(REMLIB)
# message(STATUS "Removing library ${REMLIB} from blender linking because: not configured")
list(APPEND REM_MSG ${REMLIB})
list(REMOVE_ITEM BLENDER_SORTED_LIBS ${REMLIB})
endif()
endforeach()
if(REM_MSG)
list(SORT REM_MSG)
message(STATUS "Blender Skipping: (${REM_MSG})")
endif()
target_link_libraries(blender ${BLENDER_SORTED_LIBS})
unset(SEARCHLIB)
unset(SORTLIB)
unset(REMLIB)
unset(REM_MSG)
#else()
# target_link_libraries(blender ${BLENDER_LINK_LIBS})
#endif()
target_link_libraries(blender ${BLENDER_SORTED_LIBS})
setup_liblinks(blender)

View File

@ -2,3 +2,6 @@
# Python CTests
add_subdirectory(python)
# GTest
add_subdirectory(gtests)

View File

@ -0,0 +1,13 @@
# GTest
if(WITH_TESTS)
Include(GTestTesting)
# Otherwise we get warnings here that we cant fix in external projects
remove_strict_flags()
add_subdirectory(testing)
add_subdirectory(blenlib)
endif()

View File

@ -0,0 +1,88 @@
#include "testing/testing.h"
#include "BLI_math.h"
TEST(mathutils, RGBToHSVRoundtrip)
{
float orig_rgb[3] = {0.1f, 0.2f, 0.3f};
float hsv[3], rgb[3];
rgb_to_hsv_v(orig_rgb, hsv);
hsv_to_rgb_v(hsv, rgb);
EXPECT_V3_NEAR(orig_rgb, rgb, 1e-5);
}
TEST(mathutils, RGBToHSLRoundtrip)
{
float orig_rgb[3] = {0.1f, 0.2f, 0.3f};
float hsl[3], rgb[3];
rgb_to_hsl_v(orig_rgb, hsl);
hsl_to_rgb_v(hsl, rgb);
EXPECT_V3_NEAR(orig_rgb, rgb, 1e-5);
}
TEST(mathutils, RGBToYUVRoundtrip)
{
float orig_rgb[3] = {0.1f, 0.2f, 0.3f};
float yuv[3], rgb[3];
rgb_to_yuv(orig_rgb[0], orig_rgb[1], orig_rgb[2],
&yuv[0], &yuv[1], &yuv[2]);
yuv_to_rgb(yuv[0], yuv[1], yuv[2],
&rgb[0], &rgb[1], &rgb[2]);
EXPECT_V3_NEAR(orig_rgb, rgb, 1e-4);
}
TEST(mathutils, RGBToYCCRoundtrip)
{
float orig_rgb[3] = {0.1f, 0.2f, 0.3f};
float ycc[3], rgb[3];
rgb_to_ycc(orig_rgb[0], orig_rgb[1], orig_rgb[2],
&ycc[0], &ycc[1], &ycc[2],
BLI_YCC_ITU_BT601);
ycc_to_rgb(ycc[0], ycc[1], ycc[2],
&rgb[0], &rgb[1], &rgb[2],
BLI_YCC_ITU_BT601);
EXPECT_V3_NEAR(orig_rgb, rgb, 1e-3);
rgb_to_ycc(orig_rgb[0], orig_rgb[1], orig_rgb[2],
&ycc[0], &ycc[1], &ycc[2],
BLI_YCC_ITU_BT709);
ycc_to_rgb(ycc[0], ycc[1], ycc[2],
&rgb[0], &rgb[1], &rgb[2],
BLI_YCC_ITU_BT709);
EXPECT_V3_NEAR(orig_rgb, rgb, 1e-3);
rgb_to_ycc(orig_rgb[0], orig_rgb[1], orig_rgb[2],
&ycc[0], &ycc[1], &ycc[2],
BLI_YCC_JFIF_0_255);
ycc_to_rgb(ycc[0], ycc[1], ycc[2],
&rgb[0], &rgb[1], &rgb[2],
BLI_YCC_JFIF_0_255);
EXPECT_V3_NEAR(orig_rgb, rgb, 1e-3);
}
TEST(mathutils, LinearRGBTosRGBNearZero)
{
float linear_color = 0.002f;
float srgb_color = linearrgb_to_srgb(linear_color);
EXPECT_NEAR(0.02584f, srgb_color, 1e-5);
}
TEST(mathutils, LinearRGBTosRGB)
{
float linear_color = 0.75f;
float srgb_color = linearrgb_to_srgb(linear_color);
EXPECT_NEAR(0.880824f, srgb_color, 1e-5);
}
TEST(mathutils, LinearRGBTosRGBRoundtrip)
{
const int N = 50;
int i;
for (i = 0; i < N; ++i) {
float orig_linear_color = (float) i / N;
float srgb_color = linearrgb_to_srgb(orig_linear_color);
float linear_color = srgb_to_linearrgb(srgb_color);
EXPECT_NEAR(orig_linear_color, linear_color, 1e-5);
}
}

View File

@ -0,0 +1,21 @@
#include "testing/testing.h"
#include "BLI_math.h"
TEST(mathutils, DistToLine2DSimple)
{
float p[2] = {5.0f, 1.0f},
a[2] = {0.0f, 0.0f},
b[2] = {2.0f, 0.0f};
float distance = dist_to_line_v2(p, a, b);
EXPECT_NEAR(1.0f, distance, 1e-6);
}
TEST(mathutils, DistToLineSegment2DSimple)
{
float p[2] = {3.0f, 1.0f},
a[2] = {0.0f, 0.0f},
b[2] = {2.0f, 0.0f};
float distance = dist_to_line_segment_v2(p, a, b);
EXPECT_NEAR(sqrtf(2.0f), distance, 1e-6);
}

View File

@ -0,0 +1,219 @@
#include "testing/testing.h"
extern "C" {
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "../../../source/blender/imbuf/IMB_imbuf.h"
}
/* -------------------------------------------------------------------- */
/* stubs */
extern "C" {
const char *GHOST_getUserDir(int version, const char *versionstr);
const char *GHOST_getSystemDir(int version, const char *versionstr);
#ifdef __linux__
char *zLhm65070058860608_br_find_exe(const char *default_exe);
#endif
const char *GHOST_getUserDir(int version, const char *versionstr)
{
return "/home/user";
}
const char *GHOST_getSystemDir(int version, const char *versionstr)
{
return "/system/path";
}
struct ImBuf;
void IMB_freeImBuf(struct ImBuf *ibuf) {}
#ifdef __linux__
char *zLhm65070058860608_br_find_exe(const char *default_exe)
{
return NULL;
}
#endif
}
/* -------------------------------------------------------------------- */
/* tests */
/* BLI_cleanup_path */
TEST(pathutils, PathUtilClean)
{
/* "/./" -> "/" */
{
char path[FILE_MAX] = "/a/./b/./c/./";
BLI_cleanup_path(NULL, path);
EXPECT_STREQ("/a/b/c/", path);
}
{
char path[FILE_MAX] = "/./././";
BLI_cleanup_path(NULL, path);
EXPECT_STREQ("/", path);
}
{
char path[FILE_MAX] = "/a/./././b/";
BLI_cleanup_path(NULL, path);
EXPECT_STREQ("/a/b/", path);
}
/* "//" -> "/" */
{
char path[FILE_MAX] = "a////";
BLI_cleanup_path(NULL, path);
EXPECT_STREQ("a/", path);
}
if (0) /* FIXME */
{
char path[FILE_MAX] = "./a////";
BLI_cleanup_path(NULL, path);
EXPECT_STREQ("./a/", path);
}
/* "foo/bar/../" -> "foo/" */
{
char path[FILE_MAX] = "/a/b/c/../../../";
BLI_cleanup_path(NULL, path);
EXPECT_STREQ("/", path);
}
{
char path[FILE_MAX] = "/a/../a/b/../b/c/../c/";
BLI_cleanup_path(NULL, path);
EXPECT_STREQ("/a/b/c/", path);
}
{
char path[FILE_MAX] = "//../";
BLI_cleanup_path("/a/b/c/", path);
EXPECT_STREQ("/a/b/", path);
}
}
/* BLI_path_frame */
TEST(pathutils, PathUtilFrame)
{
bool ret;
{
char path[FILE_MAX] = "";
ret = BLI_path_frame(path, 123, 1);
EXPECT_EQ(1, ret);
EXPECT_STREQ("123", path);
}
{
char path[FILE_MAX] = "";
ret = BLI_path_frame(path, 123, 12);
EXPECT_EQ(1, ret);
EXPECT_STREQ("000000000123", path);
}
{
char path[FILE_MAX] = "test_";
ret = BLI_path_frame(path, 123, 1);
EXPECT_EQ(1, ret);
EXPECT_STREQ("test_123", path);
}
{
char path[FILE_MAX] = "test_";
ret = BLI_path_frame(path, 1, 12);
EXPECT_EQ(1, ret);
EXPECT_STREQ("test_000000000001", path);
}
{
char path[FILE_MAX] = "test_############";
ret = BLI_path_frame(path, 1, 0);
EXPECT_EQ(1, ret);
EXPECT_STREQ("test_000000000001", path);
}
{
char path[FILE_MAX] = "test_#_#_middle";
ret = BLI_path_frame(path, 123, 0);
EXPECT_EQ(1, ret);
EXPECT_STREQ("test_#_123_middle", path);
}
/* intentionally fail */
{
char path[FILE_MAX] = "";
ret = BLI_path_frame(path, 123, 0);
EXPECT_EQ(0, ret);
EXPECT_STREQ("", path);
}
{
char path[FILE_MAX] = "test_middle";
ret = BLI_path_frame(path, 123, 0);
EXPECT_EQ(0, ret);
EXPECT_STREQ("test_middle", path);
}
}
/* BLI_split_dirfile */
TEST(pathutils, PathUtilSplitDirfile)
{
{
const char *path = "";
char dir[FILE_MAX], file[FILE_MAX];
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
EXPECT_STREQ("", dir);
EXPECT_STREQ("", file);
}
{
const char *path = "/";
char dir[FILE_MAX], file[FILE_MAX];
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
EXPECT_STREQ("/", dir);
EXPECT_STREQ("", file);
}
{
const char *path = "fileonly";
char dir[FILE_MAX], file[FILE_MAX];
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
EXPECT_STREQ("", dir);
EXPECT_STREQ("fileonly", file);
}
{
const char *path = "dironly/";
char dir[FILE_MAX], file[FILE_MAX];
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
EXPECT_STREQ("dironly/", dir);
EXPECT_STREQ("", file);
}
{
const char *path = "/a/b";
char dir[FILE_MAX], file[FILE_MAX];
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
EXPECT_STREQ("/a/", dir);
EXPECT_STREQ("b", file);
}
{
const char *path = "/dirtoobig/filetoobig";
char dir[5], file[5];
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
EXPECT_STREQ("/dir", dir);
EXPECT_STREQ("file", file);
BLI_split_dirfile(path, dir, file, 1, 1);
EXPECT_STREQ("", dir);
EXPECT_STREQ("", file);
}
}

View File

@ -0,0 +1,39 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# The Original Code is Copyright (C) 2014, Blender Foundation
# All rights reserved.
#
# Contributor(s): Sergey Sharybin
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
../
../../../source/blender/blenlib
../../../intern/guardedalloc
)
include_directories(${INC})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}")
BLENDER_TEST(BLI_math_color "bf_blenlib")
BLENDER_TEST(BLI_math_geom "bf_blenlib")
BLENDER_TEST(BLI_path_util "bf_blenlib;extern_wcwidth;${ZLIB_LIBRARIES}")

View File

@ -0,0 +1,48 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# The Original Code is Copyright (C) 2014, Blender Foundation
# All rights reserved.
#
# Contributor(s): Sergey Sharybin
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
../
../../../extern/libmv/third_party/gflags
../../../extern/gtest/include
)
if(WIN32)
list(APPEND INC
../../../extern/libmv/third_party/glog/src/windows
)
else()
list(APPEND INC
../../../extern/libmv/third_party/glog/src
)
endif()
set(INC_SYS
)
set(SRC
testing_main.cc
)
blender_add_lib(bf_testing_main "${SRC}" "${INC}" "${INC_SYS}")

View File

@ -0,0 +1,78 @@
#ifndef __BLENDER_TESTING_H__
#define __BLENDER_TESTING_H__
#include "glog/logging.h"
#include "gflags/gflags.h"
#include "gtest/gtest.h"
#define EXPECT_V3_NEAR(a, b, eps) \
{ \
EXPECT_NEAR(a[0], b[0], eps); \
EXPECT_NEAR(a[1], b[1], eps); \
EXPECT_NEAR(a[2], b[2], eps); \
} (void) 0
#define EXPECT_MATRIX_NEAR(a, b, tolerance) \
do { \
bool dims_match = (a.rows() == b.rows()) && (a.cols() == b.cols()); \
EXPECT_EQ(a.rows(), b.rows()) << "Matrix rows don't match."; \
EXPECT_EQ(a.cols(), b.cols()) << "Matrix cols don't match."; \
if (dims_match) { \
for (int r = 0; r < a.rows(); ++r) { \
for (int c = 0; c < a.cols(); ++c) { \
EXPECT_NEAR(a(r, c), b(r, c), tolerance) \
<< "r=" << r << ", c=" << c << "."; \
} \
} \
} \
} while(false);
#define EXPECT_MATRIX_NEAR_ZERO(a, tolerance) \
do { \
for (int r = 0; r < a.rows(); ++r) { \
for (int c = 0; c < a.cols(); ++c) { \
EXPECT_NEAR(0.0, a(r, c), tolerance) \
<< "r=" << r << ", c=" << c << "."; \
} \
} \
} while(false);
#define EXPECT_MATRIX_EQ(a, b) \
do { \
bool dims_match = (a.rows() == b.rows()) && (a.cols() == b.cols()); \
EXPECT_EQ(a.rows(), b.rows()) << "Matrix rows don't match."; \
EXPECT_EQ(a.cols(), b.cols()) << "Matrix cols don't match."; \
if (dims_match) { \
for (int r = 0; r < a.rows(); ++r) { \
for (int c = 0; c < a.cols(); ++c) { \
EXPECT_EQ(a(r, c), b(r, c)) \
<< "r=" << r << ", c=" << c << "."; \
} \
} \
} \
} while(false);
// Check that sin(angle(a, b)) < tolerance.
#define EXPECT_MATRIX_PROP(a, b, tolerance) \
do { \
bool dims_match = (a.rows() == b.rows()) && (a.cols() == b.cols()); \
EXPECT_EQ(a.rows(), b.rows()) << "Matrix rows don't match."; \
EXPECT_EQ(a.cols(), b.cols()) << "Matrix cols don't match."; \
if (dims_match) { \
double c = CosinusBetweenMatrices(a, b); \
if (c * c < 1) { \
double s = sqrt(1 - c * c); \
EXPECT_NEAR(0, s, tolerance); \
} \
} \
} while(false);
#ifdef LIBMV_NUMERIC_NUMERIC_H
template<class TMat>
double CosinusBetweenMatrices(const TMat &a, const TMat &b) {
return (a.array() * b.array()).sum() /
libmv::FrobeniusNorm(a) / libmv::FrobeniusNorm(b);
}
#endif
#endif // __BLENDER_TESTING_H__

View File

@ -0,0 +1,36 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2014 Blender Foundation.
* All rights reserved.
*
* Contributor(s): Blender Foundation,
* Sergey Sharybin
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "testing/testing.h"
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
google::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
return RUN_ALL_TESTS();
}