Slow working on bringing the old code up to standard.
- Documentation updates - most of the old files use non-Doxygen or no/minimal header comments. - Rework SimpleSuite to be more useful. - Coverity-surfaced fixes.
This commit is contained in:
parent
a9991f241a
commit
b1bbaebdac
|
@ -1,6 +1,7 @@
|
||||||
<component name="InspectionProjectProfileManager">
|
<component name="InspectionProjectProfileManager">
|
||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="ClangTidy" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||||
<option name="processCode" value="true" />
|
<option name="processCode" value="true" />
|
||||||
|
|
|
@ -44,16 +44,17 @@ set(HEADER_FILES
|
||||||
include/scsl/StringUtil.h
|
include/scsl/StringUtil.h
|
||||||
include/scsl/TLV.h
|
include/scsl/TLV.h
|
||||||
|
|
||||||
|
include/scmp/scmp.h
|
||||||
include/scmp/Math.h
|
include/scmp/Math.h
|
||||||
include/scmp/Motion2D.h
|
include/scmp/Motion2D.h
|
||||||
include/scmp/geom/Coord2D.h
|
include/scmp/geom/Coord2D.h
|
||||||
include/scmp/geom/Orientation.h
|
include/scmp/geom/Orientation.h
|
||||||
include/scmp/geom/Quaternion.h
|
include/scmp/geom/Quaternion.h
|
||||||
include/scmp/geom/Vector.h
|
include/scmp/geom/Vector.h
|
||||||
|
include/scmp/filter/Madgwick.h
|
||||||
|
|
||||||
include/sctest/Assert.h
|
include/sctest/Assert.h
|
||||||
include/sctest/Report.h
|
include/sctest/Report.h
|
||||||
include/scmp/filter/Madgwick.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(include)
|
include_directories(include)
|
||||||
|
|
|
@ -1,4 +1,26 @@
|
||||||
/// math.h provides certain useful mathematical functions.
|
///
|
||||||
|
/// \file include/scmp/Math.h
|
||||||
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
|
/// \date 2017-06-05
|
||||||
|
/// \brief Common math functions.
|
||||||
|
///
|
||||||
|
/// Arena defines a memory management backend for pre-allocating memory.
|
||||||
|
///
|
||||||
|
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||||
|
///
|
||||||
|
/// Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
/// any purpose with or without fee is hereby granted, provided that
|
||||||
|
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||||
|
///
|
||||||
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
///
|
||||||
|
|
||||||
#ifndef SCCCL_MATH_H
|
#ifndef SCCCL_MATH_H
|
||||||
#define SCCCL_MATH_H
|
#define SCCCL_MATH_H
|
||||||
|
@ -9,8 +31,8 @@
|
||||||
namespace scmp {
|
namespace scmp {
|
||||||
|
|
||||||
|
|
||||||
// MAX_RADIAN is a precomputed 2 * M_PI, and MIN_RADIAN is -2 * M_PI.
|
/// MAX_RADIAN is a precomputed 2 * M_PI. and MIN_RADIAN is -2 * M_PI.
|
||||||
constexpr double MAX_RADIAN = 2 * M_PI;
|
constexpr double MAX_RADIAN = 2 * M_PI;
|
||||||
constexpr double MIN_RADIAN = -2 * M_PI;
|
constexpr double MIN_RADIAN = -2 * M_PI;
|
||||||
constexpr double POS_HALF_RADIAN = M_PI / 2;
|
constexpr double POS_HALF_RADIAN = M_PI / 2;
|
||||||
constexpr double NEG_HALF_RADIAN = -(M_PI / 2);
|
constexpr double NEG_HALF_RADIAN = -(M_PI / 2);
|
||||||
|
@ -18,8 +40,10 @@ constexpr double NEG_HALF_RADIAN = -(M_PI / 2);
|
||||||
|
|
||||||
/// Roll m die of n sides, returning a vector of the dice.
|
/// Roll m die of n sides, returning a vector of the dice.
|
||||||
std::vector<int> Die(int m, int n);
|
std::vector<int> Die(int m, int n);
|
||||||
|
|
||||||
/// Roll m die of n sides, returning the total of the die.
|
/// Roll m die of n sides, returning the total of the die.
|
||||||
int DieTotal(int m, int n);
|
int DieTotal(int m, int n);
|
||||||
|
|
||||||
/// Roll m die of n sides, and take the total of the top k die.
|
/// Roll m die of n sides, and take the total of the top k die.
|
||||||
int BestDie(int k, int m, int n);
|
int BestDie(int k, int m, int n);
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,9 @@
|
||||||
#include <scmp/geom/Quaternion.h>
|
#include <scmp/geom/Quaternion.h>
|
||||||
|
|
||||||
|
|
||||||
/// scmp contains the wntrmute robotics code.
|
/// scmp contains the chimmering clarity math and physics code.
|
||||||
namespace scmp {
|
namespace scmp {
|
||||||
|
|
||||||
/// filter contains filtering algorithms.
|
/// filter contains filtering algorithms.
|
||||||
namespace filter {
|
namespace filter {
|
||||||
|
|
||||||
|
@ -54,30 +55,30 @@ namespace filter {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Madgwick {
|
class Madgwick {
|
||||||
public:
|
public:
|
||||||
/// The Madgwick filter is initialised with an identity quaternion.
|
/// \brief The Madgwick filter is initialised with an identity quaternion.
|
||||||
Madgwick() : deltaT(0.0), previousSensorFrame(), sensorFrame() {};
|
Madgwick() : deltaT(0.0), previousSensorFrame(), sensorFrame() {};
|
||||||
|
|
||||||
|
|
||||||
/// The Madgwick filter is initialised with a sensor frame.
|
/// \brief The Madgwick filter is initialised with a sensor frame.
|
||||||
///
|
///
|
||||||
/// \param sf A sensor frame; if zero, the sensor frame will be
|
/// \param sf A sensor frame; if zero, the sensor frame will be
|
||||||
/// initialised as an identity quaternion.
|
/// initialised as an identity quaternion.
|
||||||
Madgwick(scmp::geom::Vector<T, 3> sf) : deltaT(0.0), previousSensorFrame()
|
Madgwick(scmp::geom::Vector<T, 3> sf) : deltaT(0.0), previousSensorFrame()
|
||||||
{
|
{
|
||||||
if (!sf.isZero()) {
|
if (!sf.isZero()) {
|
||||||
sensorFrame = scmp::geom::quaternion(sf, 0.0);
|
sensorFrame = scmp::geom::quaternion<T>(sf, 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Initialise the filter with a sensor frame quaternion.
|
/// \brief Initialise the filter with a sensor frame quaternion.
|
||||||
///
|
///
|
||||||
/// \param sf A quaternion representing the current Orientation.
|
/// \param sf A quaternion representing the current Orientation.
|
||||||
Madgwick(scmp::geom::Quaternion<T> sf) :
|
Madgwick(scmp::geom::Quaternion<T> sf) :
|
||||||
deltaT(0.0), previousSensorFrame(), sensorFrame(sf) {};
|
deltaT(0.0), previousSensorFrame(), sensorFrame(sf) {};
|
||||||
|
|
||||||
|
|
||||||
/// Return the current Orientation as measured by the filter.
|
/// \brief Return the current Orientation as measured by the filter.
|
||||||
///
|
///
|
||||||
/// \return The current sensor frame.
|
/// \return The current sensor frame.
|
||||||
scmp::geom::Quaternion<T>
|
scmp::geom::Quaternion<T>
|
||||||
|
@ -87,6 +88,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Return the filter's rate of angular change from a
|
||||||
|
/// sensor frame.
|
||||||
|
///
|
||||||
/// Return the rate of change of the Orientation of the earth frame
|
/// Return the rate of change of the Orientation of the earth frame
|
||||||
/// with respect to the sensor frame.
|
/// with respect to the sensor frame.
|
||||||
///
|
///
|
||||||
|
@ -99,7 +103,7 @@ public:
|
||||||
return (this->sensorFrame * 0.5) * scmp::geom::Quaternion<T>(gyro, 0.0);
|
return (this->sensorFrame * 0.5) * scmp::geom::Quaternion<T>(gyro, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the sensor frame to a new frame.
|
/// \brief Update the sensor frame to a new frame.
|
||||||
///
|
///
|
||||||
/// \param sf The new sensor frame replacing the previous one.
|
/// \param sf The new sensor frame replacing the previous one.
|
||||||
/// \param delta The time delta since the last update.
|
/// \param delta The time delta since the last update.
|
||||||
|
@ -112,7 +116,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Update the sensor frame with a gyroscope reading.
|
/// \brief Update the sensor frame with a gyroscope reading.
|
||||||
///
|
///
|
||||||
/// \param gyro A three-dimensional vector containing gyro readings
|
/// \param gyro A three-dimensional vector containing gyro readings
|
||||||
/// as w_x, w_y, w_z.
|
/// as w_x, w_y, w_z.
|
||||||
|
@ -121,14 +125,14 @@ public:
|
||||||
UpdateAngularOrientation(const scmp::geom::Vector<T, 3> &gyro, T delta)
|
UpdateAngularOrientation(const scmp::geom::Vector<T, 3> &gyro, T delta)
|
||||||
{
|
{
|
||||||
// Ensure the delta isn't zero within a 100 μs tolerance.
|
// Ensure the delta isn't zero within a 100 μs tolerance.
|
||||||
assert(!scmp::WithinTolerance(delta, 0.0, 0.0001));
|
assert(!scmp::WithinTolerance<T>(delta, 0.0, 0.0001));
|
||||||
scmp::geom::Quaternion<T> q = this->AngularRate(gyro) * delta;
|
scmp::geom::Quaternion<T> q = this->AngularRate(gyro) * delta;
|
||||||
|
|
||||||
this->UpdateFrame(this->sensorFrame + q, delta);
|
this->UpdateFrame(this->sensorFrame + q, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Retrieve a vector of the Euler angles in ZYX Orientation.
|
/// \brief Retrieve a vector of the Euler angles in ZYX Orientation.
|
||||||
///
|
///
|
||||||
/// \return A vector of Euler angles as <ψ, θ, ϕ>.
|
/// \return A vector of Euler angles as <ψ, θ, ϕ>.
|
||||||
scmp::geom::Vector<T, 3>
|
scmp::geom::Vector<T, 3>
|
||||||
|
@ -144,11 +148,11 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Madgwickd is a shorthand alias for a Madgwick<double>.
|
/// \brief Madgwickd is a shorthand alias for a Madgwick<double>.
|
||||||
typedef Madgwick<double> Madgwickd;
|
using Madgwickd = Madgwick<double>;
|
||||||
|
|
||||||
/// Madgwickf is a shorthand alias for a Madgwick<float>.
|
/// \brief Madgwickf is a shorthand alias for a Madgwick<float>.
|
||||||
typedef Madgwick<float> Madgwickf;
|
using Madgwickf = Madgwick<float>;
|
||||||
|
|
||||||
|
|
||||||
} // namespace filter
|
} // namespace filter
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
///
|
||||||
|
/// \file include/scmp/scmp.h
|
||||||
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
|
/// \date 2023-10-19
|
||||||
|
/// \brief Aggregated scmp include header.
|
||||||
|
///
|
||||||
|
/// Copyright 2023 K. Isom <kyle@imap.cc>
|
||||||
|
///
|
||||||
|
/// Permission to use, copy, modify, and/or distribute this software for
|
||||||
|
/// any purpose with or without fee is hereby granted, provided that
|
||||||
|
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||||
|
///
|
||||||
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifndef SCSL_SCMP_H
|
||||||
|
#define SCSL_SCMP_H
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Shimmering Clarity Math & Physics toolkit.
|
||||||
|
namespace scmp {
|
||||||
|
|
||||||
|
} // namespace scmp
|
||||||
|
|
||||||
|
|
||||||
|
#endif //SCSL_SCMP_H
|
|
@ -21,12 +21,6 @@
|
||||||
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
/// PERFORMANCE OF THIS SOFTWARE.
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
///
|
///
|
||||||
/// \section PLATFORM SUPPORT
|
|
||||||
///
|
|
||||||
/// Arena will build on the major platforms, but memory-mapped files are only
|
|
||||||
/// supported on Unix-like systems. File I/O on Windows, for example, reads the
|
|
||||||
/// file into an allocated arena. See Arena::Open for more details.
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef KIMODEM_ARENA_H
|
#ifndef KIMODEM_ARENA_H
|
||||||
#define KIMODEM_ARENA_H
|
#define KIMODEM_ARENA_H
|
||||||
|
|
|
@ -35,16 +35,11 @@ namespace sctest {
|
||||||
#define SCTEST_CHECK_FALSE(x) if ((x)) { return false; }
|
#define SCTEST_CHECK_FALSE(x) if ((x)) { return false; }
|
||||||
#define SCTEST_CHECK_EQ(x, y) if ((x) != (y)) { return false; }
|
#define SCTEST_CHECK_EQ(x, y) if ((x) != (y)) { return false; }
|
||||||
#define SCTEST_CHECK_NE(x, y) if ((x) == (y)) { return false; }
|
#define SCTEST_CHECK_NE(x, y) if ((x) == (y)) { return false; }
|
||||||
#define SCTEST_CHECK_ZERO(x) if ((x) != 0) { return false; }
|
|
||||||
#define SCTEST_CHECK_GTZ(x) if ((x) > 0) { return false; }
|
|
||||||
#define SCTEST_CHECK_GEZ(x) if ((x) >= 0) { return false; }
|
|
||||||
#define SCTEST_CHECK_LEZ(x) if ((x) <= 0) { return false; }
|
|
||||||
#define SCTEST_CHECK_LTZ(x) if ((x) < 0) { return false; }
|
|
||||||
#define SCTEST_CHECK_FEQ(x, y) { float eps; scmp::DefaultEpsilon(eps); if (!scmp::WithinTolerance((x), (y), eps)) { return false; }}
|
#define SCTEST_CHECK_FEQ(x, y) { float eps; scmp::DefaultEpsilon(eps); if (!scmp::WithinTolerance((x), (y), eps)) { return false; }}
|
||||||
#define SCTEST_CHECK_DEQ(x, y) { double eps; scmp::DefaultEpsilon(eps); if (!scmp::WithinTolerance((x), (y), eps)) { return false; }}
|
#define SCTEST_CHECK_DEQ(x, y) { double eps; scmp::DefaultEpsilon(eps); if (!scmp::WithinTolerance((x), (y), eps)) { return false; }}
|
||||||
|
|
||||||
#define SCTEST_CHECK_FEQ_EPS(x, y, eps) { if (!scmp::WithinTolerance((x), (y), eps)) { return false; }}
|
#define SCTEST_CHECK_FEQ_EPS(x, y, eps) { if (!scmp::WithinTolerance<float>((x), (y), eps)) { return false; }}
|
||||||
#define SCTEST_CHECK_DEQ_EPS(x, y, eps) { if (!scmp::WithinTolerance((x), (y), eps)) { return false; }}
|
#define SCTEST_CHECK_DEQ_EPS(x, y, eps) { if (!scmp::WithinTolerance<double>((x), (y), eps)) { return false; }}
|
||||||
|
|
||||||
|
|
||||||
} // namespace sctest
|
} // namespace sctest
|
||||||
|
|
|
@ -28,21 +28,42 @@
|
||||||
|
|
||||||
namespace sctest {
|
namespace sctest {
|
||||||
|
|
||||||
typedef struct _Report {
|
|
||||||
// Failing stores the number of failing tests; for tests added
|
|
||||||
// with AddTest, this is a test that returned false. For tests
|
|
||||||
// added with AddFailingTest, this is a test that returned true.
|
|
||||||
size_t Failing;
|
|
||||||
|
|
||||||
// Total is the number of tests registered during the last run.
|
class Report {
|
||||||
size_t Total;
|
public:
|
||||||
|
/// \brief Failing returns the count of failed tests.
|
||||||
|
///
|
||||||
|
/// \details If a test is run and expected to pass, but fails,
|
||||||
|
/// it is marked as failed. If a test is expected to
|
||||||
|
/// fail, but passes, it is marked as failed.
|
||||||
|
///
|
||||||
|
/// \return The number of tests that failed.
|
||||||
|
size_t Failing() const;
|
||||||
|
|
||||||
std::chrono::time_point<std::chrono::steady_clock> Start;
|
/// \brief Total is the number of tests registered.
|
||||||
std::chrono::time_point<std::chrono::steady_clock> End;
|
size_t Total() const;
|
||||||
std::chrono::duration<double> Duration;
|
|
||||||
|
void Failed();
|
||||||
|
void AddTest(size_t testCount = 0);
|
||||||
|
void Reset(size_t testCount = 0);
|
||||||
|
|
||||||
|
void Start();
|
||||||
|
void End();
|
||||||
|
std::chrono::duration<double, std::milli>
|
||||||
|
Elapsed() const;
|
||||||
|
|
||||||
|
Report();
|
||||||
|
private:
|
||||||
|
size_t failing{};
|
||||||
|
size_t total{};
|
||||||
|
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> start;
|
||||||
|
std::chrono::time_point<std::chrono::steady_clock> end;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const Report &report);
|
||||||
|
} // end namespace sctest
|
||||||
|
|
||||||
_Report();
|
|
||||||
} Report;
|
|
||||||
|
|
||||||
} // end namespace test
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,30 +1,28 @@
|
||||||
//
|
///
|
||||||
// Project: scccl
|
/// \file SimpleSuite.h
|
||||||
// File: include/test/SimpleSuite.h
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
// Author: Kyle Isom
|
/// \date 2017-06-05
|
||||||
// Date: 2017-06-05
|
/// \brief Defines a simple unit testing framework.
|
||||||
// Namespace: test
|
///
|
||||||
//
|
/// Copyright 2017 K. Isom <kyle@imap.cc>
|
||||||
// SimpleSuite.h defines the SimpleSuite class for unit testing.
|
///
|
||||||
//
|
/// Permission to use, copy, modify, and/or distribute this software for
|
||||||
// Copyright 2017 Kyle Isom <kyle@imap.cc>
|
/// any purpose with or without fee is hereby granted, provided that
|
||||||
//
|
/// the above copyright notice and this permission notice appear in all /// copies.
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
///
|
||||||
// you may not use this file except in compliance with the License.
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
// You may obtain a copy of the License at
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
//
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
//
|
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
// See the License for the specific language governing permissions and
|
///
|
||||||
// limitations under the License.
|
|
||||||
#ifndef SCTEST_SIMPLESUITE_H
|
#ifndef SCTEST_SIMPLESUITE_H
|
||||||
#define SCTEST_SIMPLESUITE_H
|
#define SCTEST_SIMPLESUITE_H
|
||||||
|
|
||||||
// SimpleSuite.h
|
|
||||||
// This header file defines the interface for a simple suite of tests.
|
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -34,54 +32,91 @@
|
||||||
|
|
||||||
namespace sctest {
|
namespace sctest {
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
std::string name;
|
|
||||||
std::function<bool(void)> test;
|
|
||||||
} TestCase;
|
|
||||||
|
|
||||||
|
/// \brief UnitTest describes a single unit test. It is a predicate:
|
||||||
|
/// did the test pass?
|
||||||
|
struct UnitTest {
|
||||||
|
/// What name should be shown when running tests?
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
/// This is the test function to be run.
|
||||||
|
std::function<bool()> test;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief SimpleSuite is a test-running harness for simple tests.
|
||||||
|
///
|
||||||
|
/// A simple test is defined as a test that takes no arguments and
|
||||||
|
/// returns a boolean status where true indicates the test has passed.
|
||||||
class SimpleSuite {
|
class SimpleSuite {
|
||||||
public:
|
public:
|
||||||
SimpleSuite();
|
SimpleSuite();
|
||||||
|
|
||||||
// Silence suppresses output.
|
/// \brief Silence suppresses output.
|
||||||
void Silence(void) { quiet = true; }
|
void Silence();
|
||||||
|
|
||||||
// Setup defines a setup function; this should be a predicate. This function
|
/// \brief Define a suite setup function.
|
||||||
// is called at the start of the Run method, before tests are run.
|
///
|
||||||
|
/// If present, this setup function is called at the start of
|
||||||
|
/// the Run method, before tests are run. It should be a
|
||||||
|
/// predicate: if it returns false, tests automatically fail.
|
||||||
void Setup(std::function<bool(void)> setupFn) { fnSetup = setupFn; }
|
void Setup(std::function<bool(void)> setupFn) { fnSetup = setupFn; }
|
||||||
|
|
||||||
// Teardown defines a teardown function; this should be a predicate. This
|
/// \brief Define a teardown function.
|
||||||
// function is called at the end of the Run method, after all tests have run.
|
///
|
||||||
|
/// If present, this teardown function is called at the end of
|
||||||
|
/// the Run method, after all tests have run.
|
||||||
void Teardown(std::function<bool(void)> teardownFn) { fnTeardown = teardownFn; }
|
void Teardown(std::function<bool(void)> teardownFn) { fnTeardown = teardownFn; }
|
||||||
|
|
||||||
// AddTest is used to add a test that is expected to return true.
|
/// \brief Register a new simple test.
|
||||||
void AddTest(std::string, std::function<bool(void)>);
|
///
|
||||||
|
/// \param label The text that will identify test when
|
||||||
|
/// running.
|
||||||
|
/// \param test This test should return true if the test has
|
||||||
|
/// passed.
|
||||||
|
void AddTest(std::string label, std::function<bool(void)> test);
|
||||||
|
|
||||||
// AddFailingTest is used to add a test that is expected to return false.
|
/// \brief Register a test that is expected to return false.
|
||||||
void AddFailingTest(std::string, std::function<bool(void)>);
|
///
|
||||||
|
/// \param label The text that will identify test when
|
||||||
|
/// running.
|
||||||
|
/// \param test This test should return false if the test has
|
||||||
|
/// passed.
|
||||||
|
void AddFailingTest(std::string label, std::function<bool(void)> test);
|
||||||
|
|
||||||
bool Run(void);
|
/// \brief Run all the registered tests.
|
||||||
|
///
|
||||||
|
/// \return True if all tests have passed.
|
||||||
|
bool Run();
|
||||||
|
|
||||||
// Reporting methods.
|
/// Reporting methods.
|
||||||
|
|
||||||
// Reset clears the report statistics.
|
/// \brief Reset clears the report statistics.
|
||||||
void Reset(void) { report.Failing = report.Total = 0; hasRun = false; };
|
///
|
||||||
|
/// Reset will preserve the setup and teardown functions, just
|
||||||
|
/// resetting the suite's internal state.
|
||||||
|
void Reset();
|
||||||
|
|
||||||
// IsReportReady returns true if a report is ready.
|
/// \brief
|
||||||
bool IsReportReady(void) { return hasRun; }
|
// HasRun returns true if a report is ready.
|
||||||
|
bool HasRun() const;
|
||||||
|
|
||||||
// Report returns a Report.
|
// Report returns a Report.
|
||||||
Report GetReport(void);
|
Report GetReport(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool quiet;
|
bool quiet;
|
||||||
std::function<bool(void)> fnSetup, fnTeardown;
|
std::function<bool(void)> fnSetup, fnTeardown;
|
||||||
std::vector<TestCase> tests;
|
std::vector<UnitTest> tests;
|
||||||
|
|
||||||
// Report functions.
|
// Report functions.
|
||||||
Report report;
|
Report report;
|
||||||
bool hasRun; // Have the tests been run yet?
|
bool hasRun; // Have the tests been run yet?
|
||||||
|
bool hasPassed;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace test
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, SimpleSuite &suite);
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace sctest
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -109,12 +109,19 @@ Buffer::Append(const std::string s)
|
||||||
bool
|
bool
|
||||||
Buffer::Append(const uint8_t *data, const size_t datalen)
|
Buffer::Append(const uint8_t *data, const size_t datalen)
|
||||||
{
|
{
|
||||||
|
if (datalen == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto resized = false;
|
auto resized = false;
|
||||||
auto newCap = this->mustGrow(datalen);
|
auto newCap = this->mustGrow(datalen);
|
||||||
|
|
||||||
if (newCap > 0) {
|
if (newCap > 0) {
|
||||||
this->Resize(newCap);
|
this->Resize(newCap);
|
||||||
resized = true;
|
resized = true;
|
||||||
|
} else if (this->contents == nullptr) {
|
||||||
|
this->Resize(this->capacity);
|
||||||
|
resized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(this->contents != nullptr);
|
assert(this->contents != nullptr);
|
||||||
|
|
|
@ -22,16 +22,96 @@
|
||||||
/// PERFORMANCE OF THIS SOFTWARE.
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
#include <sctest/Report.h>
|
#include <sctest/Report.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace sctest {
|
namespace sctest {
|
||||||
|
|
||||||
|
|
||||||
_Report::_Report()
|
Report::Report()
|
||||||
: Failing (0), Total(0), Start(std::chrono::steady_clock::now()),
|
{
|
||||||
End(std::chrono::steady_clock::now()), Duration(0) {}
|
this->Reset(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // end namespace test
|
size_t
|
||||||
|
Report::Failing() const
|
||||||
|
{
|
||||||
|
return this->failing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t
|
||||||
|
Report::Total() const
|
||||||
|
{
|
||||||
|
return this->total;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Report::Failed()
|
||||||
|
{
|
||||||
|
this->failing++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Report::AddTest(size_t testCount)
|
||||||
|
{
|
||||||
|
this->total += testCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Report::Reset(size_t testCount)
|
||||||
|
{
|
||||||
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
this->total = testCount;
|
||||||
|
this->failing = 0;
|
||||||
|
|
||||||
|
this->Start();
|
||||||
|
this->end = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Report::Start()
|
||||||
|
{
|
||||||
|
this->start = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
Report::End()
|
||||||
|
{
|
||||||
|
this->end = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::chrono::duration<double, std::milli>
|
||||||
|
Report::Elapsed() const
|
||||||
|
{
|
||||||
|
return this->end - this->start;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream &os, const Report &report)
|
||||||
|
{
|
||||||
|
auto elapsed = report.Elapsed();
|
||||||
|
|
||||||
|
os << report.Total() - report.Failing() << "/"
|
||||||
|
<< report.Total() << " tests passed in "
|
||||||
|
<< std::setw(3) << elapsed.count() << "ms";
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
} // end namespace sctest
|
|
@ -20,7 +20,6 @@
|
||||||
/// PERFORMANCE OF THIS SOFTWARE.
|
/// PERFORMANCE OF THIS SOFTWARE.
|
||||||
///
|
///
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <sctest/SimpleSuite.h>
|
#include <sctest/SimpleSuite.h>
|
||||||
|
@ -39,14 +38,22 @@ SimpleSuite::SimpleSuite()
|
||||||
: quiet(false), fnSetup(stub), fnTeardown(stub), tests(),
|
: quiet(false), fnSetup(stub), fnTeardown(stub), tests(),
|
||||||
report(), hasRun(false)
|
report(), hasRun(false)
|
||||||
{
|
{
|
||||||
|
this->Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SimpleSuite::Silence()
|
||||||
|
{
|
||||||
|
// Silence will fall.
|
||||||
|
quiet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
SimpleSuite::AddTest(std::string name, std::function<bool()> test)
|
SimpleSuite::AddTest(std::string name, std::function<bool()> test)
|
||||||
{
|
{
|
||||||
TestCase test_case = {name, test};
|
UnitTest const test_case = {name, test};
|
||||||
tests.push_back(test_case);
|
tests.push_back(test_case);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +62,7 @@ void
|
||||||
SimpleSuite::AddFailingTest(std::string name, std::function<bool()> test)
|
SimpleSuite::AddFailingTest(std::string name, std::function<bool()> test)
|
||||||
{
|
{
|
||||||
// auto ntest = [&test]() { return !test(); };
|
// auto ntest = [&test]() { return !test(); };
|
||||||
TestCase test_case = {name, [&test]() { return !test(); }};
|
UnitTest test_case = {name, [&test]() { return !test(); }};
|
||||||
tests.push_back(test_case);
|
tests.push_back(test_case);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,38 +70,55 @@ SimpleSuite::AddFailingTest(std::string name, std::function<bool()> test)
|
||||||
bool
|
bool
|
||||||
SimpleSuite::Run()
|
SimpleSuite::Run()
|
||||||
{
|
{
|
||||||
report.Start = std::chrono::steady_clock::now();
|
report.Reset(this->tests.size());
|
||||||
|
|
||||||
unless(quiet) { std::cout << "Setting up the tests.\n"; }
|
unless(quiet) { std::cout << "Setting up the tests.\n"; }
|
||||||
unless(fnSetup()) { return false; }
|
unless(fnSetup()) { return false; }
|
||||||
|
|
||||||
// Reset the failed test counts.
|
this->hasRun = true;
|
||||||
report.Failing = 0;
|
this->hasPassed = true;
|
||||||
|
|
||||||
bool result = true;
|
for (size_t i = 0; i < this->report.Total() && this->hasPassed; i++) {
|
||||||
hasRun = true;
|
const UnitTest testCase = this->tests.at(i);
|
||||||
report.Total = tests.size();
|
|
||||||
for (size_t i = 0; i < report.Total && result; i++) {
|
|
||||||
TestCase tc = tests.at(i);
|
|
||||||
unless(quiet) {
|
unless(quiet) {
|
||||||
std::cout << "[" << i + 1 << "/" << report.Total << "] Running test " << tc.name << ": ";
|
std::cout << "[" << i + 1 << "/"
|
||||||
|
<< this -> report.Total()
|
||||||
|
<< "] Running test "
|
||||||
|
<< testCase.name << ": ";
|
||||||
}
|
}
|
||||||
|
|
||||||
result = tc.test();
|
this->hasPassed = testCase.test();
|
||||||
if (quiet) { continue; }
|
if (quiet) { continue; }
|
||||||
|
|
||||||
if (result) {
|
if (this->hasPassed) {
|
||||||
std::cout << "[PASS]";
|
std::cout << "[PASS]";
|
||||||
} else {
|
} else {
|
||||||
std::cout << "[FAIL]";
|
std::cout << "[FAIL]";
|
||||||
report.Failing++;
|
report.Failed();
|
||||||
}
|
}
|
||||||
std::cout << "\n";
|
std::cout << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
unless(quiet) { std::cout << "Tearing down the tests.\n"; }
|
unless(quiet) { std::cout << "Tearing down the tests.\n"; }
|
||||||
unless(fnTeardown()) { return false; }
|
unless(fnTeardown()) { return false; }
|
||||||
report.End = std::chrono::steady_clock::now();
|
report.End();
|
||||||
return result;
|
return this->hasPassed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
SimpleSuite::Reset()
|
||||||
|
{
|
||||||
|
this->report.Reset(0);
|
||||||
|
this->hasRun = false;
|
||||||
|
this->hasPassed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
SimpleSuite::HasRun() const
|
||||||
|
{
|
||||||
|
return this->hasRun;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,4 +129,17 @@ SimpleSuite::GetReport()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::ostream &
|
||||||
|
operator<<(std::ostream &os, SimpleSuite &suite)
|
||||||
|
{
|
||||||
|
if (suite.HasRun()) {
|
||||||
|
os << "OK: " << suite.GetReport();
|
||||||
|
} else {
|
||||||
|
os << "Test suite hasn't run.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // end namespace sctest
|
} // end namespace sctest
|
157
test/coord2d.cc
157
test/coord2d.cc
|
@ -25,21 +25,23 @@
|
||||||
|
|
||||||
#include <scmp/Math.h>
|
#include <scmp/Math.h>
|
||||||
#include <scmp/geom/Coord2D.h>
|
#include <scmp/geom/Coord2D.h>
|
||||||
#include <sctest/SimpleSuite.h>
|
|
||||||
#include <sctest/Checks.h>
|
#include <sctest/Checks.h>
|
||||||
|
#include <sctest/SimpleSuite.h>
|
||||||
|
|
||||||
using namespace scmp::geom;
|
using namespace scmp::geom;
|
||||||
using namespace sctest;
|
using namespace sctest;
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
#define CHECK_ROTATE(theta, expected) if (!scmp::WithinTolerance(scmp::RotateRadians((double)theta, 0), (double)expected, (double)0.0001)) { \
|
#define CHECK_ROTATE(theta, expected) if (!scmp::WithinTolerance(scmp::RotateRadians((double)theta, 0), (double)expected, (double)0.0001)) { \
|
||||||
std::cerr << "Expected " << theta << " to wrap to " << expected << std::endl; \
|
std::cerr << "Expected " << theta << " to wrap to " << expected << "\n"; \
|
||||||
std::cerr << " have " << scmp::RotateRadians(theta, 0) << std::endl; \
|
std::cerr << " have " << scmp::RotateRadians(theta, 0) << "\n"; \
|
||||||
return false; \
|
return false; \
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
geom_validate_angular_rotation(void)
|
bool
|
||||||
|
geomValidateAngularRotation()
|
||||||
{
|
{
|
||||||
CHECK_ROTATE(0, 0);
|
CHECK_ROTATE(0, 0);
|
||||||
CHECK_ROTATE(M_PI/4, M_PI/4);
|
CHECK_ROTATE(M_PI/4, M_PI/4);
|
||||||
|
@ -54,17 +56,18 @@ geom_validate_angular_rotation(void)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
geom_conversion_identities(void)
|
bool
|
||||||
|
geomConversionIdentities()
|
||||||
{
|
{
|
||||||
Point2D points[4] = {
|
const std::array<Point2D,4> points = {
|
||||||
Point2D(1, 0),
|
Point2D(1, 0),
|
||||||
Point2D(0, 1),
|
Point2D(0, 1),
|
||||||
Point2D(-1, 0),
|
Point2D(-1, 0),
|
||||||
Point2D(0, -1)
|
Point2D(0, -1)
|
||||||
};
|
};
|
||||||
|
|
||||||
Polar2D polars[4] = {
|
const std::array<Polar2D,4> polars = {
|
||||||
Polar2D(1, 0),
|
Polar2D(1, 0),
|
||||||
Polar2D(1, scmp::DegreesToRadiansD(90)),
|
Polar2D(1, scmp::DegreesToRadiansD(90)),
|
||||||
Polar2D(1, scmp::DegreesToRadiansD(180)),
|
Polar2D(1, scmp::DegreesToRadiansD(180)),
|
||||||
|
@ -72,92 +75,103 @@ geom_conversion_identities(void)
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto i = 0; i < 4; i++) {
|
for (auto i = 0; i < 4; i++) {
|
||||||
Polar2D pol(points[i]);
|
const Polar2D pol(points.at(i));
|
||||||
if (pol != polars[i]) {
|
if (pol != polars.at(i)) {
|
||||||
std::cerr << "! measured value outside tolerance (" << i << ")" << std::endl;
|
std::cerr << "! measured value outside tolerance ("
|
||||||
std::cerr << " " << points[i] << " → " << pol << " ← " << polars[i] << std::endl;
|
<< i << ")\n";
|
||||||
|
std::cerr << " " << points.at(i) << " → " << pol
|
||||||
|
<< " ← " << polars.at(i) << "\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point2D pt(pol);
|
const Point2D point(pol);
|
||||||
SCTEST_CHECK(pt == points[i]);
|
SCTEST_CHECK(point == points.at(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
geom_verify_basic_properties(void)
|
|
||||||
{
|
|
||||||
Point2D p1(1, 1);
|
|
||||||
Point2D p2(2, 2);
|
|
||||||
Point2D p3(3, 3);
|
|
||||||
|
|
||||||
SCTEST_CHECK((p1 + p2) == p3);
|
bool
|
||||||
SCTEST_CHECK((p3 - p2) == p1);
|
geomVerifyBasicProperties()
|
||||||
|
{
|
||||||
|
const Point2D pt1(1, 1);
|
||||||
|
const Point2D pt2(2, 2);
|
||||||
|
const Point2D pt3(3, 3);
|
||||||
|
|
||||||
|
SCTEST_CHECK((pt1 + pt2) == pt3);
|
||||||
|
SCTEST_CHECK((pt3 - pt2) == pt1);
|
||||||
|
|
||||||
// commutative
|
// commutative
|
||||||
SCTEST_CHECK((p1 + p2) == (p2 + p1));
|
SCTEST_CHECK((pt1 + pt2) == (pt2 + pt1));
|
||||||
SCTEST_CHECK((p1 + p3) == (p3 + p1));
|
SCTEST_CHECK((pt1 + pt3) == (pt3 + pt1));
|
||||||
SCTEST_CHECK((p2 + p3) == (p3 + p2));
|
SCTEST_CHECK((pt2 + pt3) == (pt3 + pt2));
|
||||||
|
|
||||||
// associative
|
// associative
|
||||||
SCTEST_CHECK(((p1 + p2) + p3) == (p1 + (p2 + p3)));
|
SCTEST_CHECK(((pt1 + pt2) + pt3) == (pt1 + (pt2 + pt3)));
|
||||||
|
|
||||||
// transitive
|
// transitive
|
||||||
Point2D p4(1, 1);
|
const Point2D pt4(1, 1);
|
||||||
Point2D p5(1, 1);
|
const Point2D pt5(1, 1);
|
||||||
SCTEST_CHECK(p1 == p4);
|
SCTEST_CHECK(pt1 == pt4);
|
||||||
SCTEST_CHECK(p4 == p5);
|
SCTEST_CHECK(pt4 == pt5);
|
||||||
SCTEST_CHECK(p1 == p5);
|
SCTEST_CHECK(pt1 == pt5);
|
||||||
|
|
||||||
// scaling
|
// scaling
|
||||||
Point2D p6(2, 3);
|
const Point2D pt6(2, 3);
|
||||||
Point2D p7(8, 12);
|
const Point2D pt7(8, 12);
|
||||||
SCTEST_CHECK((p6 * 4) == p7);
|
SCTEST_CHECK((pt6 * 4) == pt7);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
geom_compare_point2d(void)
|
|
||||||
{
|
|
||||||
Point2D p1(1, 1);
|
|
||||||
Point2D p2(1, 1);
|
|
||||||
Point2D p3(0, 1);
|
|
||||||
|
|
||||||
SCTEST_CHECK(p1 == p2);
|
bool
|
||||||
SCTEST_CHECK_FALSE(p2 == p3);
|
geomComparePoint2D()
|
||||||
|
{
|
||||||
|
const Point2D pt1(1, 1);
|
||||||
|
const Point2D pt2(1, 1);
|
||||||
|
const Point2D pt3(0, 1);
|
||||||
|
|
||||||
|
SCTEST_CHECK(pt1 == pt2);
|
||||||
|
SCTEST_CHECK_FALSE(pt2 == pt3);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
geom_rotate_point2d(void)
|
bool
|
||||||
|
geomRotatePoint2D()
|
||||||
{
|
{
|
||||||
Point2D vertices[4] = {
|
std::array<Point2D, 4> vertices = {
|
||||||
Point2D(1, 0), // θ = 0
|
Point2D(1, 0), // θ = 0
|
||||||
Point2D(0, 1), // θ = π/2
|
Point2D(0, 1), // θ = π/2
|
||||||
Point2D(-1, 0), // θ = π
|
Point2D(-1, 0), // θ = π
|
||||||
Point2D(0, -1) // θ = 3π/2
|
Point2D(0, -1) // θ = 3π/2
|
||||||
};
|
};
|
||||||
|
|
||||||
Point2D vertex;
|
for (auto i = 0; i < 4; i++) {
|
||||||
vertices[0].Rotate(vertex, 1.5708);
|
auto first = i % 4;
|
||||||
|
auto expected = (i + 1) % 4;
|
||||||
|
|
||||||
if (vertex != vertices[1]) {
|
Point2D vertex;
|
||||||
std::cerr << "expected: " << vertices[1] << std::endl;
|
vertices.at(first).Rotate(vertex, 1.5708);
|
||||||
std::cerr << " have: " << vertex << std::endl;
|
|
||||||
return false;
|
if (vertex != vertices.at(expected)) {
|
||||||
|
std::cerr << "expected: " << expected << "\n";
|
||||||
|
std::cerr << " have: " << vertex << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
geom_rotate_points_about_origin(void)
|
bool
|
||||||
|
geomRotatePointsAboutOrigin()
|
||||||
{
|
{
|
||||||
Point2D origin(3, 3);
|
Point2D origin(3, 3);
|
||||||
double theta = 0;
|
double theta = 0;
|
||||||
|
|
||||||
std::vector<Polar2D> vertices {
|
const std::vector<Polar2D> vertices {
|
||||||
Polar2D(2, 0),
|
Polar2D(2, 0),
|
||||||
Polar2D(1.41421, 2.35619),
|
Polar2D(1.41421, 2.35619),
|
||||||
Polar2D(1.41421, -2.35619)
|
Polar2D(1.41421, -2.35619)
|
||||||
|
@ -203,25 +217,22 @@ geom_rotate_points_about_origin(void)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void)
|
main()
|
||||||
{
|
{
|
||||||
SimpleSuite ts;
|
SimpleSuite suite;
|
||||||
ts.AddTest("geom_validate_angular_rotation", geom_validate_angular_rotation);
|
suite.AddTest("geomValidateAngularRotation", geomValidateAngularRotation);
|
||||||
ts.AddTest("geom_conversion_identities", geom_conversion_identities);
|
suite.AddTest("geomConversionIdentities", geomConversionIdentities);
|
||||||
ts.AddTest("geom_verify_basic_properties", geom_verify_basic_properties);
|
suite.AddTest("geomVerifyBasicProperties", geomVerifyBasicProperties);
|
||||||
ts.AddTest("geom_compare_point2d", geom_compare_point2d);
|
suite.AddTest("geomComparePoint2D", geomComparePoint2D);
|
||||||
ts.AddTest("geom_rotate_point2d", geom_rotate_point2d);
|
suite.AddTest("geomRotatePoint2D", geomRotatePoint2D);
|
||||||
ts.AddTest("geom_rotate_points_about_origin", geom_rotate_points_about_origin);
|
suite.AddTest("geomRotatePointsAboutOrigin", geomRotatePointsAboutOrigin);
|
||||||
|
|
||||||
if (ts.Run()) {
|
auto result = suite.Run();
|
||||||
std::cout << "OK" << std::endl;
|
std::cout << suite << "\n";
|
||||||
return 0;
|
|
||||||
}
|
return result ? 0 : 1;
|
||||||
else {
|
|
||||||
auto r = ts.GetReport();
|
|
||||||
std::cerr << r.Failing << "/" << r.Total << " tests failed." << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
182
test/madgwick.cc
182
test/madgwick.cc
|
@ -15,23 +15,157 @@ using namespace scmp;
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
SimpleAngularOrientation()
|
SimpleAngularOrientationFloat()
|
||||||
{
|
{
|
||||||
filter::Madgwickd mf;
|
filter::Madgwickf mflt;
|
||||||
geom::Vector3d gyro{0.174533, 0.0, 0.0}; // 10° X rotation.
|
const geom::Vector3f gyro{0.174533, 0.0, 0.0}; // 10° X rotation.
|
||||||
geom::Quaterniond frame20Deg{0.984808, 0.173648, 0, 0}; // 20° final Orientation.
|
const geom::Quaternionf frame20Deg{0.984808, 0.173648, 0, 0}; // 20° final Orientation.
|
||||||
double delta = 0.00917; // assume 109 updates per second, as per the paper.
|
const float delta = 0.00917; // assume 109 updates per second, as per the paper.
|
||||||
double twentyDegrees = scmp::DegreesToRadiansD(20.0);
|
const float twentyDegrees = scmp::DegreesToRadiansF(20.0);
|
||||||
|
|
||||||
// The paper specifies a minimum of 109 IMU readings to stabilize; for
|
// The paper specifies a minimum of 109 IMU readings to stabilize; for
|
||||||
// two seconds, that means 218 updates.
|
// two seconds, that means 218 updates.
|
||||||
for (int i = 0; i < 218; i++) {
|
for (int i = 0; i < 218; i++) {
|
||||||
mf.UpdateAngularOrientation(gyro, delta);
|
mflt.UpdateAngularOrientation(gyro, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
SCTEST_CHECK_EQ(mf.Orientation(), frame20Deg);
|
SCTEST_CHECK_EQ(mflt.Orientation(), frame20Deg);
|
||||||
|
|
||||||
auto euler = mf.Euler();
|
auto euler = mflt.Euler();
|
||||||
|
SCTEST_CHECK_FEQ_EPS(euler[0], twentyDegrees, 0.01);
|
||||||
|
SCTEST_CHECK_FEQ_EPS(euler[1], 0.0, 0.01);
|
||||||
|
SCTEST_CHECK_FEQ_EPS(euler[2], 0.0, 0.01);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
SimpleAngularOrientationDouble()
|
||||||
|
{
|
||||||
|
filter::Madgwickd mflt;
|
||||||
|
const geom::Vector3d gyro{0.174533, 0.0, 0.0}; // 10° X rotation.
|
||||||
|
const geom::Quaterniond frame20Deg{0.984808, 0.173648, 0, 0}; // 20° final Orientation.
|
||||||
|
const double delta = 0.00917; // assume 109 updates per second, as per the paper.
|
||||||
|
const double twentyDegrees = scmp::DegreesToRadiansD(20.0);
|
||||||
|
|
||||||
|
// The paper specifies a minimum of 109 IMU readings to stabilize; for
|
||||||
|
// two seconds, that means 218 updates.
|
||||||
|
for (int i = 0; i < 218; i++) {
|
||||||
|
mflt.UpdateAngularOrientation(gyro, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCTEST_CHECK_EQ(mflt.Orientation(), frame20Deg);
|
||||||
|
|
||||||
|
auto euler = mflt.Euler();
|
||||||
|
SCTEST_CHECK_DEQ_EPS(euler[0], twentyDegrees, 0.01);
|
||||||
|
SCTEST_CHECK_DEQ_EPS(euler[1], 0.0, 0.01);
|
||||||
|
SCTEST_CHECK_DEQ_EPS(euler[2], 0.0, 0.01);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
SimpleAngularOrientation2InitialVector3f()
|
||||||
|
{
|
||||||
|
const geom::Vector3f initialFrame{0, 0, 0};
|
||||||
|
filter::Madgwickf mflt(initialFrame);
|
||||||
|
const geom::Vector3f gyro{0.174533, 0.0, 0.0}; // 10° X rotation.
|
||||||
|
const geom::Quaternionf frame20Deg{0.984808, 0.173648, 0, 0}; // 20° final Orientation.
|
||||||
|
const float delta = 0.00917; // assume 109 updates per second, as per the paper.
|
||||||
|
const float twentyDegrees = scmp::DegreesToRadiansF(20.0);
|
||||||
|
|
||||||
|
// The paper specifies a minimum of 109 IMU readings to stabilize; for
|
||||||
|
// two seconds, that means 218 updates.
|
||||||
|
for (int i = 0; i < 218; i++) {
|
||||||
|
mflt.UpdateAngularOrientation(gyro, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCTEST_CHECK_EQ(mflt.Orientation(), frame20Deg);
|
||||||
|
|
||||||
|
auto euler = mflt.Euler();
|
||||||
|
SCTEST_CHECK_FEQ_EPS(euler[0], twentyDegrees, 0.01);
|
||||||
|
SCTEST_CHECK_FEQ_EPS(euler[1], 0.0, 0.01);
|
||||||
|
SCTEST_CHECK_FEQ_EPS(euler[2], 0.0, 0.01);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
SimpleAngularOrientation2InitialQuaternionf()
|
||||||
|
{
|
||||||
|
const auto initialFrame = geom::quaternionf_from_euler({0, 0, 0});
|
||||||
|
filter::Madgwickf mflt(initialFrame);
|
||||||
|
const geom::Vector3f gyro{0.174533, 0.0, 0.0}; // 10° X rotation.
|
||||||
|
const geom::Quaternionf frame20Deg{0.984808, 0.173648, 0, 0}; // 20° final Orientation.
|
||||||
|
const float delta = 0.00917; // assume 109 updates per second, as per the paper.
|
||||||
|
const float twentyDegrees = scmp::DegreesToRadiansF(20.0);
|
||||||
|
|
||||||
|
// The paper specifies a minimum of 109 IMU readings to stabilize; for
|
||||||
|
// two seconds, that means 218 updates.
|
||||||
|
for (int i = 0; i < 218; i++) {
|
||||||
|
mflt.UpdateAngularOrientation(gyro, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCTEST_CHECK_EQ(mflt.Orientation(), frame20Deg);
|
||||||
|
|
||||||
|
auto euler = mflt.Euler();
|
||||||
|
SCTEST_CHECK_FEQ_EPS(euler[0], twentyDegrees, 0.01);
|
||||||
|
SCTEST_CHECK_FEQ_EPS(euler[1], 0.0, 0.01);
|
||||||
|
SCTEST_CHECK_FEQ_EPS(euler[2], 0.0, 0.01);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
SimpleAngularOrientation2InitialVector3d()
|
||||||
|
{
|
||||||
|
const geom::Vector3d initialFrame{0, 0, 0};
|
||||||
|
filter::Madgwickd mflt(initialFrame);
|
||||||
|
const geom::Vector3d gyro{0.174533, 0.0, 0.0}; // 10° X rotation.
|
||||||
|
const geom::Quaterniond frame20Deg{0.984808, 0.173648, 0, 0}; // 20° final Orientation.
|
||||||
|
const double delta = 0.00917; // assume 109 updates per second, as per the paper.
|
||||||
|
const double twentyDegrees = scmp::DegreesToRadiansD(20.0);
|
||||||
|
|
||||||
|
// The paper specifies a minimum of 109 IMU readings to stabilize; for
|
||||||
|
// two seconds, that means 218 updates.
|
||||||
|
for (int i = 0; i < 218; i++) {
|
||||||
|
mflt.UpdateAngularOrientation(gyro, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCTEST_CHECK_EQ(mflt.Orientation(), frame20Deg);
|
||||||
|
|
||||||
|
auto euler = mflt.Euler();
|
||||||
|
SCTEST_CHECK_DEQ_EPS(euler[0], twentyDegrees, 0.01);
|
||||||
|
SCTEST_CHECK_DEQ_EPS(euler[1], 0.0, 0.01);
|
||||||
|
SCTEST_CHECK_DEQ_EPS(euler[2], 0.0, 0.01);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
SimpleAngularOrientation2InitialQuaterniond()
|
||||||
|
{
|
||||||
|
const auto initialFrame = geom::quaterniond_from_euler({0, 0, 0});
|
||||||
|
filter::Madgwickd mflt(initialFrame);
|
||||||
|
const geom::Vector3d gyro{0.174533, 0.0, 0.0}; // 10° X rotation.
|
||||||
|
const geom::Quaterniond frame20Deg{0.984808, 0.173648, 0, 0}; // 20° final Orientation.
|
||||||
|
const double delta = 0.00917; // assume 109 updates per second, as per the paper.
|
||||||
|
const double twentyDegrees = scmp::DegreesToRadiansD(20.0);
|
||||||
|
|
||||||
|
// The paper specifies a minimum of 109 IMU readings to stabilize; for
|
||||||
|
// two seconds, that means 218 updates.
|
||||||
|
for (int i = 0; i < 218; i++) {
|
||||||
|
mflt.UpdateAngularOrientation(gyro, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCTEST_CHECK_EQ(mflt.Orientation(), frame20Deg);
|
||||||
|
|
||||||
|
auto euler = mflt.Euler();
|
||||||
SCTEST_CHECK_DEQ_EPS(euler[0], twentyDegrees, 0.01);
|
SCTEST_CHECK_DEQ_EPS(euler[0], twentyDegrees, 0.01);
|
||||||
SCTEST_CHECK_DEQ_EPS(euler[1], 0.0, 0.01);
|
SCTEST_CHECK_DEQ_EPS(euler[1], 0.0, 0.01);
|
||||||
SCTEST_CHECK_DEQ_EPS(euler[2], 0.0, 0.01);
|
SCTEST_CHECK_DEQ_EPS(euler[2], 0.0, 0.01);
|
||||||
|
@ -43,21 +177,27 @@ SimpleAngularOrientation()
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
|
||||||
sctest::SimpleSuite suite;
|
sctest::SimpleSuite suite;
|
||||||
|
|
||||||
suite.AddTest("SimpleAngularOrientation", SimpleAngularOrientation);
|
suite.AddTest("SimpleAngularOrientationDouble",
|
||||||
|
SimpleAngularOrientationFloat);
|
||||||
|
suite.AddTest("SimpleAngularOrientationDouble",
|
||||||
|
SimpleAngularOrientationDouble);
|
||||||
|
suite.AddTest("SimpleAngularOrientationDouble (iniital vector3f)",
|
||||||
|
SimpleAngularOrientation2InitialVector3f);
|
||||||
|
suite.AddTest("SimpleAngularOrientationDouble (iniital vector3d)",
|
||||||
|
SimpleAngularOrientation2InitialVector3d);
|
||||||
|
suite.AddTest("SimpleAngularOrientationDouble (iniital quaternionf)",
|
||||||
|
SimpleAngularOrientation2InitialQuaternionf);
|
||||||
|
suite.AddTest("SimpleAngularOrientationDouble (iniital quaterniond)",
|
||||||
|
SimpleAngularOrientation2InitialQuaterniond);
|
||||||
|
|
||||||
auto result = suite.Run();
|
auto result = suite.Run();
|
||||||
|
|
||||||
if (suite.IsReportReady()) {
|
std::cout << suite.GetReport() << "\n";
|
||||||
auto report = suite.GetReport();
|
return result ? 0 : 1;
|
||||||
std::cout << report.Failing << " / " << report.Total;
|
|
||||||
std::cout << " tests failed.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,40 @@
|
||||||
#include <sctest/Checks.h>
|
#include <scsl/Flag.h>
|
||||||
#include <sctest/SimpleSuite.h>
|
|
||||||
|
|
||||||
#include <scmp/Math.h>
|
#include <scmp/Math.h>
|
||||||
#include <scmp/geom/Vector.h>
|
#include <scmp/geom/Vector.h>
|
||||||
#include <scmp/geom/Orientation.h>
|
#include <scmp/geom/Orientation.h>
|
||||||
|
|
||||||
|
#include <sctest/Checks.h>
|
||||||
|
#include <sctest/SimpleSuite.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace scmp;
|
using namespace scmp;
|
||||||
using namespace sctest;
|
using namespace sctest;
|
||||||
|
|
||||||
|
|
||||||
static bool
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
UnitConversions_RadiansToDegreesF()
|
UnitConversions_RadiansToDegreesF()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 360; i++) {
|
for (int i = 0; i < 360; i++) {
|
||||||
auto deg = static_cast<float>(i);
|
auto rads = scmp::DegreesToRadiansF(i);
|
||||||
SCTEST_CHECK_FEQ(scmp::RadiansToDegreesF(scmp::DegreesToRadiansF(deg)), deg);
|
auto deg = scmp::RadiansToDegreesF(rads);
|
||||||
|
|
||||||
|
SCTEST_CHECK_FEQ(deg, static_cast<float>(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
bool
|
||||||
UnitConversions_RadiansToDegreesD()
|
UnitConversions_RadiansToDegreesD()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 360; i++) {
|
for (int i = 0; i < 360; i++) {
|
||||||
auto deg = static_cast<double>(i);
|
auto deg = static_cast<double>(i);
|
||||||
SCTEST_CHECK_DEQ(scmp::RadiansToDegreesD(scmp::DegreesToRadiansD(deg)), deg);
|
SCTEST_CHECK_DEQ(scmp::RadiansToDegreesD(scmp::DegreesToRadiansD(deg)), deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,10 +42,10 @@ UnitConversions_RadiansToDegreesD()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
bool
|
||||||
Orientation2f_Heading()
|
Orientation2f_Heading()
|
||||||
{
|
{
|
||||||
geom::Vector2f a {2.0, 2.0};
|
geom::Vector2f a{2.0, 2.0};
|
||||||
|
|
||||||
SCTEST_CHECK_FEQ(geom::Heading2f(a), scmp::DegreesToRadiansF(45));
|
SCTEST_CHECK_FEQ(geom::Heading2f(a), scmp::DegreesToRadiansF(45));
|
||||||
|
|
||||||
|
@ -45,10 +53,10 @@ Orientation2f_Heading()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
bool
|
||||||
Orientation3f_Heading()
|
Orientation3f_Heading()
|
||||||
{
|
{
|
||||||
geom::Vector3f a {2.0, 2.0, 2.0};
|
geom::Vector3f a{2.0, 2.0, 2.0};
|
||||||
|
|
||||||
SCTEST_CHECK_FEQ(geom::Heading3f(a), scmp::DegreesToRadiansF(45));
|
SCTEST_CHECK_FEQ(geom::Heading3f(a), scmp::DegreesToRadiansF(45));
|
||||||
|
|
||||||
|
@ -56,43 +64,56 @@ Orientation3f_Heading()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
bool
|
||||||
Orientation2d_Heading()
|
Orientation2d_Heading()
|
||||||
{
|
{
|
||||||
geom::Vector2d a {2.0, 2.0};
|
geom::Vector2d a{2.0, 2.0};
|
||||||
|
|
||||||
return scmp::WithinTolerance(geom::Heading2d(a), scmp::DegreesToRadiansD(45), 0.000001);
|
return scmp::WithinTolerance(geom::Heading2d(a), scmp::DegreesToRadiansD(45), 0.000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
bool
|
||||||
Orientation3d_Heading()
|
Orientation3d_Heading()
|
||||||
{
|
{
|
||||||
geom::Vector3d a {2.0, 2.0, 2.0};
|
geom::Vector3d a{2.0, 2.0, 2.0};
|
||||||
|
|
||||||
return scmp::WithinTolerance(geom::Heading3d(a), scmp::DegreesToRadiansD(45), 0.000001);
|
return scmp::WithinTolerance(geom::Heading3d(a), scmp::DegreesToRadiansD(45), 0.000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void)
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
SimpleSuite ts;
|
auto quiet = false;
|
||||||
|
auto flags = new scsl::Flags("test_orientation",
|
||||||
|
"This test validates various orientation-related components in scmp.");
|
||||||
|
flags->Register("-q", false, "suppress test output");
|
||||||
|
|
||||||
ts.AddTest("UnitConversions_RadiansToDegreesF", UnitConversions_RadiansToDegreesF);
|
auto parsed = flags->Parse(argc, argv);
|
||||||
ts.AddTest("UnitConversions_RadiansToDegreesD", UnitConversions_RadiansToDegreesD);
|
if (parsed != scsl::Flags::ParseStatus::OK) {
|
||||||
ts.AddTest("Orientation2f_Heading", Orientation2f_Heading);
|
std::cerr << "Failed to parse flags: "
|
||||||
ts.AddTest("Orientation3f_Heading", Orientation3f_Heading);
|
<< scsl::Flags::ParseStatusToString(parsed) << "\n";
|
||||||
ts.AddTest("Orientation2d_Heading", Orientation2d_Heading);
|
}
|
||||||
ts.AddTest("Orientation3d_Heading", Orientation3d_Heading);
|
|
||||||
|
|
||||||
if (ts.Run()) {
|
SimpleSuite suite;
|
||||||
std::cout << "OK" << std::endl;
|
flags->GetBool("-q", quiet);
|
||||||
return 0;
|
if (quiet) {
|
||||||
}
|
suite.Silence();
|
||||||
else {
|
|
||||||
auto r = ts.GetReport();
|
|
||||||
std::cerr << r.Failing << "/" << r.Total << " tests failed." << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suite.AddTest("UnitConversions_RadiansToDegreesF", UnitConversions_RadiansToDegreesF);
|
||||||
|
suite.AddTest("UnitConversions_RadiansToDegreesD", UnitConversions_RadiansToDegreesD);
|
||||||
|
suite.AddTest("Orientation2f_Heading", Orientation2f_Heading);
|
||||||
|
suite.AddTest("Orientation3f_Heading", Orientation3f_Heading);
|
||||||
|
suite.AddTest("Orientation2d_Heading", Orientation2d_Heading);
|
||||||
|
suite.AddTest("Orientation3d_Heading", Orientation3d_Heading);
|
||||||
|
|
||||||
|
delete flags;
|
||||||
|
auto result = suite.Run();
|
||||||
|
std::cout << suite.GetReport() << "\n";
|
||||||
|
return result ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -429,49 +429,43 @@ QuaternionMiscellanous_InitializerConstructor()
|
||||||
int
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
SimpleSuite ts;
|
SimpleSuite suite;
|
||||||
|
|
||||||
ts.AddTest("Quaternion_SelfTest", Quaternion_SelfTest);
|
suite.AddTest("Quaternion_SelfTest", Quaternion_SelfTest);
|
||||||
ts.AddTest("QuaternionMiscellanous_InitializerConstructor",
|
suite.AddTest("QuaternionMiscellanous_InitializerConstructor",
|
||||||
QuaternionMiscellanous_InitializerConstructor);
|
QuaternionMiscellanous_InitializerConstructor);
|
||||||
ts.AddTest("QuaternionMiscellaneous_SanityChecks",
|
suite.AddTest("QuaternionMiscellaneous_SanityChecks",
|
||||||
QuaternionMiscellaneous_SanityChecks);
|
QuaternionMiscellaneous_SanityChecks);
|
||||||
ts.AddTest("QuaternionMiscellaneous_OutputStream",
|
suite.AddTest("QuaternionMiscellaneous_OutputStream",
|
||||||
QuaternionMiscellaneous_OutputStream);
|
QuaternionMiscellaneous_OutputStream);
|
||||||
|
|
||||||
ts.AddTest("Quaterniond_Addition", Quaterniond_Addition);
|
suite.AddTest("Quaterniond_Addition", Quaterniond_Addition);
|
||||||
ts.AddTest("Quaterniond_Conjugate", Quaterniond_Conjugate);
|
suite.AddTest("Quaterniond_Conjugate", Quaterniond_Conjugate);
|
||||||
ts.AddTest("Quaterniond_Euler", Quaterniond_Euler);
|
suite.AddTest("Quaterniond_Euler", Quaterniond_Euler);
|
||||||
ts.AddTest("Quaterniond_Identity", Quaterniond_Identity);
|
suite.AddTest("Quaterniond_Identity", Quaterniond_Identity);
|
||||||
ts.AddTest("Quaterniond_Inverse", Quaterniond_Inverse);
|
suite.AddTest("Quaterniond_Inverse", Quaterniond_Inverse);
|
||||||
ts.AddTest("Quaterniond_Norm", Quaterniond_Norm);
|
suite.AddTest("Quaterniond_Norm", Quaterniond_Norm);
|
||||||
ts.AddTest("Quaterniond_Product", Quaterniond_Product);
|
suite.AddTest("Quaterniond_Product", Quaterniond_Product);
|
||||||
ts.AddTest("Quaterniond_Rotate", Quaterniond_Rotate);
|
suite.AddTest("Quaterniond_Rotate", Quaterniond_Rotate);
|
||||||
ts.AddTest("Quaterniond_ShortestSLERP", Quaterniond_ShortestSLERP);
|
suite.AddTest("Quaterniond_ShortestSLERP", Quaterniond_ShortestSLERP);
|
||||||
ts.AddTest("Quaterniond_ShortestSLERP2", Quaterniond_ShortestSLERP2);
|
suite.AddTest("Quaterniond_ShortestSLERP2", Quaterniond_ShortestSLERP2);
|
||||||
ts.AddTest("Quaterniond_Unit", Quaterniond_Unit);
|
suite.AddTest("Quaterniond_Unit", Quaterniond_Unit);
|
||||||
ts.AddTest("Quaterniond_UtilityCreator", Quaterniond_UtilityCreator);
|
suite.AddTest("Quaterniond_UtilityCreator", Quaterniond_UtilityCreator);
|
||||||
|
|
||||||
ts.AddTest("Quaternionf_Addition", Quaternionf_Addition);
|
suite.AddTest("Quaternionf_Addition", Quaternionf_Addition);
|
||||||
ts.AddTest("Quaternionf_Conjugate", Quaternionf_Conjugate);
|
suite.AddTest("Quaternionf_Conjugate", Quaternionf_Conjugate);
|
||||||
ts.AddTest("Quaternionf_Euler", Quaternionf_Euler);
|
suite.AddTest("Quaternionf_Euler", Quaternionf_Euler);
|
||||||
ts.AddTest("Quaternionf_Identity", Quaternionf_Identity);
|
suite.AddTest("Quaternionf_Identity", Quaternionf_Identity);
|
||||||
ts.AddTest("Quaternionf_Inverse", Quaternionf_Inverse);
|
suite.AddTest("Quaternionf_Inverse", Quaternionf_Inverse);
|
||||||
ts.AddTest("Quaternionf_Norm", Quaternionf_Norm);
|
suite.AddTest("Quaternionf_Norm", Quaternionf_Norm);
|
||||||
ts.AddTest("Quaternionf_Product", Quaternionf_Product);
|
suite.AddTest("Quaternionf_Product", Quaternionf_Product);
|
||||||
ts.AddTest("Quaternionf_Rotate", Quaternionf_Rotate);
|
suite.AddTest("Quaternionf_Rotate", Quaternionf_Rotate);
|
||||||
ts.AddTest("Quaternionf_ShortestSLERP", Quaternionf_ShortestSLERP);
|
suite.AddTest("Quaternionf_ShortestSLERP", Quaternionf_ShortestSLERP);
|
||||||
ts.AddTest("Quaternionf_ShortestSLERP2", Quaternionf_ShortestSLERP2);
|
suite.AddTest("Quaternionf_ShortestSLERP2", Quaternionf_ShortestSLERP2);
|
||||||
ts.AddTest("Quaternionf_Unit", Quaternionf_Unit);
|
suite.AddTest("Quaternionf_Unit", Quaternionf_Unit);
|
||||||
ts.AddTest("Quaternionf_UtilityCreator", Quaternionf_UtilityCreator);
|
suite.AddTest("Quaternionf_UtilityCreator", Quaternionf_UtilityCreator);
|
||||||
|
|
||||||
if (ts.Run()) {
|
auto result = suite.Run();
|
||||||
std::cout << "OK" << std::endl;
|
std::cout << suite.GetReport() << "\n";
|
||||||
return 0;
|
return result ? 0 : 1;
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto r = ts.GetReport();
|
|
||||||
std::cerr << r.Failing << "/" << r.Total << " tests failed." << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,33 @@
|
||||||
//
|
///
|
||||||
// Project: scccl
|
/// \file test/simple_suite_example.cc
|
||||||
// File: test/math/simple_suite_example.cpp
|
/// \author K. Isom <kyle@imap.cc>
|
||||||
// Author: Kyle Isom
|
/// \date 2017-06-05
|
||||||
// Date: 2017-06-05
|
///
|
||||||
//
|
/// simple_suite_example demonstrates the usage of the SimpleSuite test class
|
||||||
// simple_suite_example demonstrates the usage of the SimpleSuite test class
|
/// and serves to unit test the unit tester (qui custodiet ipsos custodes)?
|
||||||
// and serves to unit test the unit tester (qui custodiet ipsos custodes)?
|
///
|
||||||
//
|
///
|
||||||
// Copyright 2017 Kyle Isom <kyle@imap.cc>
|
/// Copyright 2017 K. Isom <kyle@imap.cc>
|
||||||
//
|
///
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
/// Permission to use, copy, modify, and/or distribute this software for
|
||||||
// you may not use this file except in compliance with the License.
|
/// any purpose with or without fee is hereby granted, provided that the
|
||||||
// You may obtain a copy of the License at
|
/// above copyright notice and this permission notice appear in all copies.
|
||||||
//
|
///
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
//
|
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
/// BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
/// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||||
// See the License for the specific language governing permissions and
|
/// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
// limitations under the License.
|
/// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
|
/// SOFTWARE.
|
||||||
|
///
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <sctest/SimpleSuite.h>
|
#include <sctest/SimpleSuite.h>
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
prepareTests()
|
prepareTests()
|
||||||
{
|
{
|
||||||
|
@ -33,6 +36,7 @@ prepareTests()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
destroyTests()
|
destroyTests()
|
||||||
{
|
{
|
||||||
|
@ -41,6 +45,7 @@ destroyTests()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool addOne() { return 1 + 1 == 2; }
|
static bool addOne() { return 1 + 1 == 2; }
|
||||||
static bool four() { return 2 + 2 == 4; }
|
static bool four() { return 2 + 2 == 4; }
|
||||||
static bool nope() { return 2 + 2 == 5; }
|
static bool nope() { return 2 + 2 == 5; }
|
||||||
|
@ -49,24 +54,14 @@ static bool nope() { return 2 + 2 == 5; }
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
sctest::SimpleSuite TestSuite;
|
sctest::SimpleSuite suite;
|
||||||
TestSuite.Setup(prepareTests);
|
suite.Setup(prepareTests);
|
||||||
TestSuite.Teardown(destroyTests);
|
suite.Teardown(destroyTests);
|
||||||
TestSuite.AddTest("1 + 1", addOne);
|
suite.AddTest("1 + 1", addOne);
|
||||||
TestSuite.AddTest("fourness", four);
|
suite.AddTest("fourness", four);
|
||||||
TestSuite.AddFailingTest("self-evident truth", nope);
|
suite.AddFailingTest("self-evident truth", nope);
|
||||||
|
auto result = suite.Run();
|
||||||
|
|
||||||
bool result = TestSuite.Run();
|
std::cout << suite.GetReport() << "\n";
|
||||||
if (TestSuite.IsReportReady()) {
|
return result ? 0 : 1;
|
||||||
auto report = TestSuite.GetReport();
|
|
||||||
std::cout << report.Failing << " / " << report.Total;
|
|
||||||
std::cout << " tests failed.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
///
|
///
|
||||||
/// \file stringutil_test.cc
|
/// \file test/stringutil_test.cc
|
||||||
/// \author kyle
|
/// \author kyle
|
||||||
/// \date 10/14/23
|
/// \date 10/14/23
|
||||||
/// \brief Ensure the stringutil functions work.
|
/// \brief Ensure the stringutil functions work.
|
||||||
|
@ -21,17 +21,21 @@
|
||||||
///
|
///
|
||||||
|
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include <scsl/StringUtil.h>
|
#include <scsl/StringUtil.h>
|
||||||
#include <sctest/Assert.h>
|
#include <sctest/Assert.h>
|
||||||
|
#include <sctest/SimpleSuite.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace scsl;
|
using namespace scsl;
|
||||||
|
|
||||||
|
|
||||||
static void
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
TestTrimming(std::string line, std::string lExpected, std::string rExpected, std::string expected)
|
TestTrimming(std::string line, std::string lExpected, std::string rExpected, std::string expected)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
|
@ -66,42 +70,17 @@ TestTrimming(std::string line, std::string lExpected, std::string rExpected, std
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static std::string
|
std::function<bool()>
|
||||||
vec2string(std::vector<std::string> v)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
|
|
||||||
ss << "(";
|
|
||||||
ss << v.size();
|
|
||||||
ss << ")";
|
|
||||||
ss << "{";
|
|
||||||
|
|
||||||
for (size_t i = 0; i < v.size(); i++) {
|
|
||||||
if (i > 0) { ss << ", "; }
|
|
||||||
ss << v[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
ss << "}";
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
TestSplit(std::string line, std::string delim, size_t maxCount, std::vector<std::string> expected)
|
TestSplit(std::string line, std::string delim, size_t maxCount, std::vector<std::string> expected)
|
||||||
{
|
{
|
||||||
std::cout << "test split\n";
|
return [line, delim, maxCount, expected]() {
|
||||||
std::cout << "\t line: \"" << line << "\"\n";
|
return U::S::SplitN(line, delim, maxCount) == expected;
|
||||||
std::cout << "\t delim: \"" << delim << "\"\n";
|
|
||||||
std::cout << "\t count: " << maxCount << "\n";
|
};
|
||||||
std::cout << "\texpect: " << vec2string(expected) << "\n";
|
|
||||||
auto result = U::S::SplitN(line, delim, maxCount);
|
|
||||||
std::cout << "\tresult: " << U::S::VectorToString(result) << "\n";
|
|
||||||
sctest::Assert(result == expected, U::S::VectorToString(result));
|
|
||||||
std::cout << "OK!\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
bool
|
||||||
TestWrapping()
|
TestWrapping()
|
||||||
{
|
{
|
||||||
std::string testLine = "A much longer line, something that can be tested with WrapText. ";
|
std::string testLine = "A much longer line, something that can be tested with WrapText. ";
|
||||||
|
@ -119,34 +98,52 @@ TestWrapping()
|
||||||
};
|
};
|
||||||
|
|
||||||
auto wrapped = U::S::WrapText(testLine, 16);
|
auto wrapped = U::S::WrapText(testLine, 16);
|
||||||
sctest::Assert(wrapped.size() == expected.size(),
|
if (wrapped.size() != expected.size()) {
|
||||||
U::S::VectorToString(wrapped) + " != " + U::S::VectorToString(expected));
|
std::cerr << U::S::VectorToString(wrapped)
|
||||||
|
<< " != "
|
||||||
|
<< U::S::VectorToString(expected)
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < wrapped.size(); i++) {
|
for (size_t i = 0; i < wrapped.size(); i++) {
|
||||||
sctest::Assert(wrapped[i] == expected[i],
|
if (wrapped[i] == expected[i]) {
|
||||||
"\"" + wrapped[i] + "\" != \"" + expected[i] + "\"");
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "[" << i << "] \"" << wrapped[i] << "\" != \""
|
||||||
|
<< expected[i] << "\"\n";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
U::S::WriteTabIndented(std::cout, wrapped, 4, true);
|
U::S::WriteTabIndented(std::cout, wrapped, 4, true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main()
|
main()
|
||||||
{
|
{
|
||||||
|
sctest::SimpleSuite suite;
|
||||||
|
|
||||||
TestTrimming(" foo\t ", "foo\t ", " foo", "foo");
|
TestTrimming(" foo\t ", "foo\t ", " foo", "foo");
|
||||||
TestTrimming(" foo\tbar ", "foo\tbar ", " foo\tbar", "foo\tbar");
|
TestTrimming(" foo\tbar ", "foo\tbar ", " foo\tbar", "foo\tbar");
|
||||||
|
|
||||||
TestSplit("abc:def:ghij:klm", ":", 0,
|
suite.AddTest("SplitN(0)", TestSplit("abc:def:ghij:klm", ":", 0,
|
||||||
std::vector<std::string>{"abc", "def", "ghij", "klm"});
|
std::vector<std::string>{"abc", "def", "ghij", "klm"}));
|
||||||
TestSplit("abc:def:ghij:klm", ":", 3,
|
suite.AddTest("SplitN(3)", TestSplit("abc:def:ghij:klm", ":", 3,
|
||||||
std::vector<std::string>{"abc", "def", "ghij:klm"});
|
std::vector<std::string>{"abc", "def", "ghij:klm"}));
|
||||||
TestSplit("abc:def:ghij:klm", ":", 2,
|
suite.AddTest("SplitN(2)", TestSplit("abc:def:ghij:klm", ":", 2,
|
||||||
std::vector<std::string>{"abc", "def:ghij:klm"});
|
std::vector<std::string>{"abc", "def:ghij:klm"}));
|
||||||
TestSplit("abc:def:ghij:klm", ":", 1,
|
suite.AddTest("SplitN(1)", TestSplit("abc:def:ghij:klm", ":", 1,
|
||||||
std::vector<std::string>{"abc:def:ghij:klm"});
|
std::vector<std::string>{"abc:def:ghij:klm"}));
|
||||||
TestSplit("abc::def:ghi", ":", 0,
|
suite.AddTest("SplitN(0) with empty element",
|
||||||
std::vector<std::string>{"abc", "", "def", "ghi"});
|
TestSplit("abc::def:ghi", ":", 0,
|
||||||
|
std::vector<std::string>{"abc", "", "def", "ghi"}));
|
||||||
TestWrapping();
|
suite.AddTest("TextWrapping", TestWrapping);
|
||||||
|
auto result = suite.Run();
|
||||||
|
std::cout << suite.GetReport() << "\n";
|
||||||
|
return result ? 0 : 1;
|
||||||
}
|
}
|
126
test/vector.cc
126
test/vector.cc
|
@ -428,71 +428,65 @@ Vector3DoubleTests_CrossProduct()
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void)
|
main()
|
||||||
{
|
{
|
||||||
SimpleSuite ts;
|
SimpleSuite suite;
|
||||||
ts.AddTest("Vector3Miscellaneous_ExtractionOperator3d",
|
suite.AddTest("Vector3Miscellaneous_ExtractionOperator3d",
|
||||||
Vector3Miscellaneous_ExtractionOperator3d);
|
Vector3Miscellaneous_ExtractionOperator3d);
|
||||||
ts.AddTest("Vector3Miscellaneous_ExtractionOperator3f",
|
suite.AddTest("Vector3Miscellaneous_ExtractionOperator3f",
|
||||||
Vector3Miscellaneous_ExtractionOperator3f);
|
Vector3Miscellaneous_ExtractionOperator3f);
|
||||||
ts.AddTest("Vector3Miscellaneous_SetEpsilon",
|
suite.AddTest("Vector3Miscellaneous_SetEpsilon",
|
||||||
Vector3Miscellaneous_SetEpsilon);
|
Vector3Miscellaneous_SetEpsilon);
|
||||||
ts.AddTest("Vector3FloatTests_Magnitude",
|
suite.AddTest("Vector3FloatTests_Magnitude",
|
||||||
Vector3FloatTests_Magnitude);
|
Vector3FloatTests_Magnitude);
|
||||||
ts.AddTest("Vector3FloatTests_Equality",
|
suite.AddTest("Vector3FloatTests_Equality",
|
||||||
Vector3FloatTests_Equality);
|
Vector3FloatTests_Equality);
|
||||||
ts.AddTest("Vector3FloatTests_Addition",
|
suite.AddTest("Vector3FloatTests_Addition",
|
||||||
Vector3FloatTests_Addition);
|
Vector3FloatTests_Addition);
|
||||||
ts.AddTest("Vector3FloatTests_Subtraction",
|
suite.AddTest("Vector3FloatTests_Subtraction",
|
||||||
Vector3FloatTests_Subtraction);
|
Vector3FloatTests_Subtraction);
|
||||||
ts.AddTest("Vector3FloatTests_ScalarMultiplication",
|
suite.AddTest("Vector3FloatTests_ScalarMultiplication",
|
||||||
Vector3FloatTests_ScalarMultiplication);
|
Vector3FloatTests_ScalarMultiplication);
|
||||||
ts.AddTest("Vector3FloatTests_ScalarDivision",
|
suite.AddTest("Vector3FloatTests_ScalarDivision",
|
||||||
Vector3FloatTests_ScalarDivision);
|
Vector3FloatTests_ScalarDivision);
|
||||||
ts.AddTest("Vector3FloatTests_DotProduct",
|
suite.AddTest("Vector3FloatTests_DotProduct",
|
||||||
Vector3FloatTests_DotProduct);
|
Vector3FloatTests_DotProduct);
|
||||||
ts.AddTest("Vector3FloatTests_UnitVector",
|
suite.AddTest("Vector3FloatTests_UnitVector",
|
||||||
Vector3FloatTests_UnitVector);
|
Vector3FloatTests_UnitVector);
|
||||||
ts.AddTest("Vector3FloatTests_Angle",
|
suite.AddTest("Vector3FloatTests_Angle",
|
||||||
Vector3FloatTests_Angle);
|
Vector3FloatTests_Angle);
|
||||||
ts.AddTest("Vector3FloatTests_ParallelOrthogonalVectors",
|
suite.AddTest("Vector3FloatTests_ParallelOrthogonalVectors",
|
||||||
Vector3FloatTests_ParallelOrthogonalVectors);
|
Vector3FloatTests_ParallelOrthogonalVectors);
|
||||||
ts.AddTest("Vector3FloatTests_Projections",
|
suite.AddTest("Vector3FloatTests_Projections",
|
||||||
Vector3FloatTests_Projections);
|
Vector3FloatTests_Projections);
|
||||||
ts.AddTest("Vector3FloatTests_CrossProduct",
|
suite.AddTest("Vector3FloatTests_CrossProduct",
|
||||||
Vector3FloatTests_CrossProduct);
|
Vector3FloatTests_CrossProduct);
|
||||||
ts.AddTest("Vector3DoubleTests_Magnitude",
|
suite.AddTest("Vector3DoubleTests_Magnitude",
|
||||||
Vector3DoubleTests_Magnitude);
|
Vector3DoubleTests_Magnitude);
|
||||||
ts.AddTest("Vector3DoubleTests_Equality",
|
suite.AddTest("Vector3DoubleTests_Equality",
|
||||||
Vector3DoubleTests_Equality);
|
Vector3DoubleTests_Equality);
|
||||||
ts.AddTest("Vector3DoubleTests_Addition",
|
suite.AddTest("Vector3DoubleTests_Addition",
|
||||||
Vector3DoubleTests_Addition);
|
Vector3DoubleTests_Addition);
|
||||||
ts.AddTest("Vector3DoubleTests_Subtraction",
|
suite.AddTest("Vector3DoubleTests_Subtraction",
|
||||||
Vector3DoubleTests_Subtraction);
|
Vector3DoubleTests_Subtraction);
|
||||||
ts.AddTest("Vector3DoubleTests_ScalarMultiplication",
|
suite.AddTest("Vector3DoubleTests_ScalarMultiplication",
|
||||||
Vector3DoubleTests_ScalarMultiplication);
|
Vector3DoubleTests_ScalarMultiplication);
|
||||||
ts.AddTest("Vector3DoubleTests_ScalarDivision",
|
suite.AddTest("Vector3DoubleTests_ScalarDivision",
|
||||||
Vector3DoubleTests_ScalarDivision);
|
Vector3DoubleTests_ScalarDivision);
|
||||||
ts.AddTest("Vector3DoubleTests_DotProduct",
|
suite.AddTest("Vector3DoubleTests_DotProduct",
|
||||||
Vector3DoubleTests_DotProduct);
|
Vector3DoubleTests_DotProduct);
|
||||||
ts.AddTest("Vector3DoubleTests_UnitVector",
|
suite.AddTest("Vector3DoubleTests_UnitVector",
|
||||||
Vector3DoubleTests_UnitVector);
|
Vector3DoubleTests_UnitVector);
|
||||||
ts.AddTest("Vector3DoubleTests_Angle",
|
suite.AddTest("Vector3DoubleTests_Angle",
|
||||||
Vector3DoubleTests_Angle);
|
Vector3DoubleTests_Angle);
|
||||||
ts.AddTest("Vector3DoubleTests_ParallelOrthogonalVectors",
|
suite.AddTest("Vector3DoubleTests_ParallelOrthogonalVectors",
|
||||||
Vector3DoubleTests_ParallelOrthogonalVectors);
|
Vector3DoubleTests_ParallelOrthogonalVectors);
|
||||||
ts.AddTest("Vector3DoubleTests_Projections",
|
suite.AddTest("Vector3DoubleTests_Projections",
|
||||||
Vector3DoubleTests_Projections);
|
Vector3DoubleTests_Projections);
|
||||||
ts.AddTest("Vector3DoubleTests_CrossProduct",
|
suite.AddTest("Vector3DoubleTests_CrossProduct",
|
||||||
Vector3DoubleTests_CrossProduct);
|
Vector3DoubleTests_CrossProduct);
|
||||||
|
auto result = suite.Run();
|
||||||
if (ts.Run()) {
|
|
||||||
std::cout << "OK" << std::endl;
|
std::cout << suite.GetReport() << "\n";
|
||||||
return 0;
|
return result ? 0 : 1;
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto r = ts.GetReport();
|
|
||||||
std::cerr << r.Failing << "/" << r.Total << " tests failed." << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue