core: turn olivecore into liboakcore.so with a pure C ABI
liboakcore is now a shared library that exposes only a C ABI: - every value class (Rational, TimeRange, Color, Bezier, AudioParams, SampleBuffer) and the free-function groups (StringUtils, fraction utils, Timecode) is wrapped in an opaque-handle C API under core/include/olive/core/oakcore/ (init/copy/free + self-first functions), implemented in core/src/capi/ - consumers keep the original C++ API unchanged through same-name wrapper classes that hold the handle and forward across the C boundary; original implementations moved to core/src/oliveimpl (namespace olive::core::internal) and are hidden from export - TimeRangeList/TimeRangeListFrameIterator are reimplemented inline over the wrapper (iterators/containers don't cross C ABI) - generic Value container stays internal (unused by consumers) and is no longer part of the public umbrella header - hidden visibility + OAKCORE_BUILD export macro; nm shows zero olive::* symbols exported - install into the platform's standard libdir (GNUInstallDirs); Windows DLLs next to the executables, macOS into the app bundle - TimelineWorkArea::in/out/length now return by value: the wrapped TimeRange getters return values, and forwarding them through const references dangled (found via RenderWorkerFootageTest crash) - tests: 9 new pure C ABI test executables (oakcore_*_test) covering every public C function; 4 stale legacy core tests removed (they targeted a long-renamed API and were never built due to a malformed option() that also kept OLIVECORE_BUILD_TESTS off) - CI/CD: oakcore.dll staged for NSIS, liboakcore.so added to the AppImage validation list, build-tree DLL copies on Windows
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
// Pure C API test for oakcore/audioparams.h: no gtest, no C++ wrappers.
|
||||
// oakcore/audioparams.h includes oakcore/rational.h for the OakRational
|
||||
// handles crossing the boundary.
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include "olive/core/oakcore/audioparams.h"
|
||||
|
||||
// Mirror of render/channellayout.h values, spelled out so this test only
|
||||
// includes the C API header.
|
||||
static const uint64_t k_layout_mono = 0x4;
|
||||
static const uint64_t k_layout_stereo = 0x3;
|
||||
static const uint64_t k_layout_2_1 = 0x103;
|
||||
static const uint64_t k_layout_5_point1 = 0x60F;
|
||||
static const uint64_t k_layout_7_point1 = 0x63F;
|
||||
|
||||
// Mirror of render/sampleformat.h enum values.
|
||||
static const int k_format_invalid = -1;
|
||||
static const int k_format_u8_p = 0;
|
||||
static const int k_format_f32_p = 4;
|
||||
static const int k_format_s16 = 7;
|
||||
static const int k_format_f64 = 11;
|
||||
|
||||
int main()
|
||||
{
|
||||
// Default constructor: invalid parameters with footage defaults
|
||||
{
|
||||
OakAudioParams *p = oakcore_audioparams_create_invalid();
|
||||
assert(p != nullptr);
|
||||
assert(oakcore_audioparams_is_valid(p) == 0);
|
||||
assert(oakcore_audioparams_sample_rate(p) == 0);
|
||||
assert(oakcore_audioparams_channel_layout(p) == 0);
|
||||
assert(oakcore_audioparams_channel_count(p) == 0);
|
||||
assert(oakcore_audioparams_format(p) == k_format_invalid);
|
||||
assert(oakcore_audioparams_bytes_per_sample_per_channel(p) == 0);
|
||||
assert(oakcore_audioparams_bits_per_sample(p) == 0);
|
||||
assert(oakcore_audioparams_enabled(p) == 1);
|
||||
assert(oakcore_audioparams_stream_index(p) == 0);
|
||||
assert(oakcore_audioparams_duration(p) == 0);
|
||||
|
||||
OakRational *tb = oakcore_audioparams_time_base(p);
|
||||
assert(oakcore_rational_is_null(tb) == 1);
|
||||
oakcore_rational_free(tb);
|
||||
|
||||
oakcore_audioparams_free(p);
|
||||
}
|
||||
|
||||
// Full constructor: 48000 Hz stereo s16
|
||||
OakAudioParams *p =
|
||||
oakcore_audioparams_create(48000, k_layout_stereo, k_format_s16);
|
||||
assert(p != nullptr);
|
||||
assert(oakcore_audioparams_is_valid(p) == 1);
|
||||
assert(oakcore_audioparams_sample_rate(p) == 48000);
|
||||
assert(oakcore_audioparams_channel_layout(p) == k_layout_stereo);
|
||||
assert(oakcore_audioparams_channel_count(p) == 2);
|
||||
assert(oakcore_audioparams_format(p) == k_format_s16);
|
||||
assert(oakcore_audioparams_bytes_per_sample_per_channel(p) == 2);
|
||||
assert(oakcore_audioparams_bits_per_sample(p) == 16);
|
||||
assert(oakcore_audioparams_enabled(p) == 1);
|
||||
assert(oakcore_audioparams_stream_index(p) == 0);
|
||||
assert(oakcore_audioparams_duration(p) == 0);
|
||||
|
||||
// Timebase defaults to 1/sample_rate
|
||||
{
|
||||
OakRational *tb = oakcore_audioparams_time_base(p);
|
||||
assert(oakcore_rational_numerator(tb) == 1);
|
||||
assert(oakcore_rational_denominator(tb) == 48000);
|
||||
oakcore_rational_free(tb);
|
||||
|
||||
OakRational *srtb = oakcore_audioparams_sample_rate_as_time_base(p);
|
||||
assert(oakcore_rational_numerator(srtb) == 1);
|
||||
assert(oakcore_rational_denominator(srtb) == 48000);
|
||||
oakcore_rational_free(srtb);
|
||||
}
|
||||
|
||||
// Setters / getters
|
||||
oakcore_audioparams_set_sample_rate(p, 44100);
|
||||
assert(oakcore_audioparams_sample_rate(p) == 44100);
|
||||
oakcore_audioparams_set_sample_rate(p, 48000);
|
||||
assert(oakcore_audioparams_sample_rate(p) == 48000);
|
||||
|
||||
// Changing the layout recalculates the channel count
|
||||
oakcore_audioparams_set_channel_layout(p, k_layout_mono);
|
||||
assert(oakcore_audioparams_channel_layout(p) == k_layout_mono);
|
||||
assert(oakcore_audioparams_channel_count(p) == 1);
|
||||
oakcore_audioparams_set_channel_layout(p, k_layout_5_point1);
|
||||
assert(oakcore_audioparams_channel_count(p) == 6);
|
||||
oakcore_audioparams_set_channel_layout(p, k_layout_stereo);
|
||||
assert(oakcore_audioparams_channel_count(p) == 2);
|
||||
|
||||
{
|
||||
OakRational *tb = oakcore_rational_create_nd(1, 1000);
|
||||
oakcore_audioparams_set_time_base(p, tb);
|
||||
oakcore_rational_free(tb);
|
||||
|
||||
OakRational *got = oakcore_audioparams_time_base(p);
|
||||
assert(oakcore_rational_numerator(got) == 1);
|
||||
assert(oakcore_rational_denominator(got) == 1000);
|
||||
oakcore_rational_free(got);
|
||||
|
||||
// Restore the default 1/48000 timebase
|
||||
tb = oakcore_rational_create_nd(1, 48000);
|
||||
oakcore_audioparams_set_time_base(p, tb);
|
||||
oakcore_rational_free(tb);
|
||||
}
|
||||
|
||||
oakcore_audioparams_set_format(p, k_format_f32_p);
|
||||
assert(oakcore_audioparams_format(p) == k_format_f32_p);
|
||||
assert(oakcore_audioparams_bytes_per_sample_per_channel(p) == 4);
|
||||
assert(oakcore_audioparams_bits_per_sample(p) == 32);
|
||||
oakcore_audioparams_set_format(p, k_format_s16);
|
||||
assert(oakcore_audioparams_bytes_per_sample_per_channel(p) == 2);
|
||||
|
||||
oakcore_audioparams_set_enabled(p, 0);
|
||||
assert(oakcore_audioparams_enabled(p) == 0);
|
||||
oakcore_audioparams_set_enabled(p, 1);
|
||||
assert(oakcore_audioparams_enabled(p) == 1);
|
||||
|
||||
oakcore_audioparams_set_stream_index(p, 3);
|
||||
assert(oakcore_audioparams_stream_index(p) == 3);
|
||||
oakcore_audioparams_set_stream_index(p, 0);
|
||||
|
||||
const int64_t big_duration = int64_t(1) << 40;
|
||||
oakcore_audioparams_set_duration(p, big_duration);
|
||||
assert(oakcore_audioparams_duration(p) == big_duration);
|
||||
oakcore_audioparams_set_duration(p, 0);
|
||||
|
||||
// Time/sample/byte conversions (48000 Hz, 2 channels, 2 bytes/sample)
|
||||
assert(oakcore_audioparams_time_to_samples(p, 1.0) == 48000);
|
||||
assert(oakcore_audioparams_time_to_samples(p, 0.5) == 24000);
|
||||
assert(oakcore_audioparams_time_to_samples(p, -1.0) == -48000);
|
||||
assert(oakcore_audioparams_time_to_samples(p, 0.0) == 0);
|
||||
|
||||
assert(oakcore_audioparams_samples_to_bytes_per_channel(p, 100) == 200);
|
||||
assert(oakcore_audioparams_samples_to_bytes(p, 100) == 400);
|
||||
assert(oakcore_audioparams_samples_to_bytes(p, 0) == 0);
|
||||
|
||||
assert(oakcore_audioparams_time_to_bytes_per_channel(p, 1.0) == 96000);
|
||||
assert(oakcore_audioparams_time_to_bytes(p, 1.0) == 192000);
|
||||
|
||||
assert(oakcore_audioparams_bytes_to_samples(p, 400) == 100);
|
||||
assert(oakcore_audioparams_bytes_to_samples(p, 0) == 0);
|
||||
|
||||
// Rational-taking overloads
|
||||
{
|
||||
OakRational *half = oakcore_rational_create_nd(1, 2);
|
||||
assert(oakcore_audioparams_time_to_samples_rational(p, half) == 24000);
|
||||
assert(oakcore_audioparams_time_to_bytes_rational(p, half) == 96000);
|
||||
assert(oakcore_audioparams_time_to_bytes_per_channel_rational(p, half) ==
|
||||
48000);
|
||||
oakcore_rational_free(half);
|
||||
}
|
||||
|
||||
// Rational-returning conversions
|
||||
{
|
||||
OakRational *t = oakcore_audioparams_samples_to_time(p, 48000);
|
||||
assert(oakcore_rational_numerator(t) == 1);
|
||||
assert(oakcore_rational_denominator(t) == 1);
|
||||
oakcore_rational_free(t);
|
||||
|
||||
t = oakcore_audioparams_samples_to_time(p, 24000);
|
||||
assert(oakcore_rational_numerator(t) == 1);
|
||||
assert(oakcore_rational_denominator(t) == 2);
|
||||
oakcore_rational_free(t);
|
||||
|
||||
t = oakcore_audioparams_bytes_to_time(p, 192000);
|
||||
assert(oakcore_rational_numerator(t) == 1);
|
||||
assert(oakcore_rational_denominator(t) == 1);
|
||||
oakcore_rational_free(t);
|
||||
|
||||
t = oakcore_audioparams_bytes_per_channel_to_time(p, 96000);
|
||||
assert(oakcore_rational_numerator(t) == 1);
|
||||
assert(oakcore_rational_denominator(t) == 1);
|
||||
oakcore_rational_free(t);
|
||||
}
|
||||
|
||||
// Copy + equality
|
||||
{
|
||||
OakAudioParams *copy = oakcore_audioparams_copy(p);
|
||||
assert(copy != nullptr);
|
||||
assert(oakcore_audioparams_equals(p, copy) == 1);
|
||||
assert(oakcore_audioparams_equals(copy, copy) == 1);
|
||||
|
||||
oakcore_audioparams_set_sample_rate(copy, 96000);
|
||||
assert(oakcore_audioparams_equals(p, copy) == 0);
|
||||
oakcore_audioparams_set_sample_rate(copy, 48000);
|
||||
assert(oakcore_audioparams_equals(p, copy) == 1);
|
||||
|
||||
OakAudioParams *invalid = oakcore_audioparams_create_invalid();
|
||||
assert(oakcore_audioparams_equals(p, invalid) == 0);
|
||||
assert(oakcore_audioparams_equals(invalid, invalid) == 1);
|
||||
oakcore_audioparams_free(invalid);
|
||||
|
||||
oakcore_audioparams_free(copy);
|
||||
}
|
||||
|
||||
// Supported channel layouts / sample rates
|
||||
{
|
||||
assert(oakcore_audioparams_supported_channel_layout_count() == 5);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(0) == k_layout_mono);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(1) ==
|
||||
k_layout_stereo);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(2) == k_layout_2_1);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(3) ==
|
||||
k_layout_5_point1);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(4) ==
|
||||
k_layout_7_point1);
|
||||
// Out-of-range indices return 0
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(-1) == 0);
|
||||
assert(oakcore_audioparams_supported_channel_layout_at(5) == 0);
|
||||
|
||||
assert(oakcore_audioparams_supported_sample_rate_count() == 10);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(0) == 8000);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(6) == 44100);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(7) == 48000);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(9) == 96000);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(-1) == 0);
|
||||
assert(oakcore_audioparams_supported_sample_rate_at(10) == 0);
|
||||
}
|
||||
|
||||
oakcore_audioparams_free(p);
|
||||
|
||||
std::printf("oakcore_audioparams_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "olive/core/oakcore/bezier.h"
|
||||
|
||||
static int double_eq(double a, double b)
|
||||
{
|
||||
return fabs(a - b) < 1e-9;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Default constructor: everything zeroed
|
||||
OakBezier *b = oakcore_bezier_create();
|
||||
assert(b != NULL);
|
||||
assert(double_eq(oakcore_bezier_x(b), 0.0));
|
||||
assert(double_eq(oakcore_bezier_y(b), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_x(b), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_y(b), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp2_x(b), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp2_y(b), 0.0));
|
||||
|
||||
// Setters write through to the getters
|
||||
oakcore_bezier_set_x(b, 1.5);
|
||||
oakcore_bezier_set_y(b, -2.25);
|
||||
oakcore_bezier_set_cp1_x(b, 0.1);
|
||||
oakcore_bezier_set_cp1_y(b, 0.2);
|
||||
oakcore_bezier_set_cp2_x(b, 0.3);
|
||||
oakcore_bezier_set_cp2_y(b, 0.4);
|
||||
assert(double_eq(oakcore_bezier_x(b), 1.5));
|
||||
assert(double_eq(oakcore_bezier_y(b), -2.25));
|
||||
assert(double_eq(oakcore_bezier_cp1_x(b), 0.1));
|
||||
assert(double_eq(oakcore_bezier_cp1_y(b), 0.2));
|
||||
assert(double_eq(oakcore_bezier_cp2_x(b), 0.3));
|
||||
assert(double_eq(oakcore_bezier_cp2_y(b), 0.4));
|
||||
|
||||
// x/y constructor: control points stay zeroed
|
||||
OakBezier *xy = oakcore_bezier_create_xy(3.0, 4.0);
|
||||
assert(double_eq(oakcore_bezier_x(xy), 3.0));
|
||||
assert(double_eq(oakcore_bezier_y(xy), 4.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_x(xy), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_y(xy), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp2_x(xy), 0.0));
|
||||
assert(double_eq(oakcore_bezier_cp2_y(xy), 0.0));
|
||||
|
||||
// Full constructor
|
||||
OakBezier *full =
|
||||
oakcore_bezier_create_full(1.0, 2.0, 0.25, 0.5, 0.75, 1.0);
|
||||
assert(double_eq(oakcore_bezier_x(full), 1.0));
|
||||
assert(double_eq(oakcore_bezier_y(full), 2.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_x(full), 0.25));
|
||||
assert(double_eq(oakcore_bezier_cp1_y(full), 0.5));
|
||||
assert(double_eq(oakcore_bezier_cp2_x(full), 0.75));
|
||||
assert(double_eq(oakcore_bezier_cp2_y(full), 1.0));
|
||||
|
||||
// Copy: same values, independent storage
|
||||
OakBezier *dup = oakcore_bezier_copy(full);
|
||||
assert(double_eq(oakcore_bezier_x(dup), 1.0));
|
||||
assert(double_eq(oakcore_bezier_cp2_y(dup), 1.0));
|
||||
oakcore_bezier_set_x(dup, 9.0);
|
||||
oakcore_bezier_set_cp1_y(dup, 8.0);
|
||||
assert(double_eq(oakcore_bezier_x(full), 1.0));
|
||||
assert(double_eq(oakcore_bezier_cp1_y(full), 0.5));
|
||||
|
||||
// Quadratic evaluation: endpoints and x->t->y round trip
|
||||
assert(double_eq(oakcore_bezier_quadratic_tto_y(0.0, 0.5, 1.0, 0.0), 0.0));
|
||||
assert(double_eq(oakcore_bezier_quadratic_tto_y(0.0, 0.5, 1.0, 1.0), 1.0));
|
||||
{
|
||||
const double x = 0.3;
|
||||
const double t =
|
||||
oakcore_bezier_quadratic_xto_t(x, 0.0, 0.25, 1.0);
|
||||
const double y =
|
||||
oakcore_bezier_quadratic_tto_y(0.0, 0.75, 1.0, t);
|
||||
assert(t >= 0.0 && t <= 1.0);
|
||||
// xto_t solves on the x curve; feeding the same x back must hold
|
||||
assert(fabs(oakcore_bezier_quadratic_tto_y(0.0, 0.25, 1.0, t) - x) <
|
||||
1e-5);
|
||||
(void) y;
|
||||
}
|
||||
// xto_t clamps x into [a, c] instead of diverging
|
||||
{
|
||||
const double t_lo = oakcore_bezier_quadratic_xto_t(-5.0, 0.0, 0.5, 1.0);
|
||||
const double t_hi = oakcore_bezier_quadratic_xto_t(5.0, 0.0, 0.5, 1.0);
|
||||
assert(t_lo >= 0.0 && t_lo <= 1.0);
|
||||
assert(t_hi >= 0.0 && t_hi <= 1.0);
|
||||
}
|
||||
|
||||
// Cubic evaluation: endpoints and x->t->y round trip
|
||||
assert(double_eq(oakcore_bezier_cubic_tto_y(0.0, 0.25, 0.75, 1.0, 0.0),
|
||||
0.0));
|
||||
assert(double_eq(oakcore_bezier_cubic_tto_y(0.0, 0.25, 0.75, 1.0, 1.0),
|
||||
1.0));
|
||||
{
|
||||
const double x = 0.6;
|
||||
const double t =
|
||||
oakcore_bezier_cubic_xto_t(x, 0.0, 0.1, 0.9, 1.0);
|
||||
const double y =
|
||||
oakcore_bezier_cubic_tto_y(0.0, 0.8, 0.2, 1.0, t);
|
||||
assert(t >= 0.0 && t <= 1.0);
|
||||
assert(fabs(oakcore_bezier_cubic_tto_y(0.0, 0.1, 0.9, 1.0, t) - x) <
|
||||
1e-5);
|
||||
(void) y;
|
||||
}
|
||||
// xto_t clamps x into [a, d] instead of diverging
|
||||
{
|
||||
const double t_lo =
|
||||
oakcore_bezier_cubic_xto_t(-5.0, 0.0, 0.25, 0.75, 1.0);
|
||||
const double t_hi =
|
||||
oakcore_bezier_cubic_xto_t(5.0, 0.0, 0.25, 0.75, 1.0);
|
||||
assert(t_lo >= 0.0 && t_lo <= 1.0);
|
||||
assert(t_hi >= 0.0 && t_hi <= 1.0);
|
||||
}
|
||||
|
||||
// Ownership: every handle released exactly once
|
||||
oakcore_bezier_free(b);
|
||||
oakcore_bezier_free(xy);
|
||||
oakcore_bezier_free(full);
|
||||
oakcore_bezier_free(dup);
|
||||
oakcore_bezier_free(NULL);
|
||||
|
||||
printf("oakcore_bezier_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,351 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
/**
|
||||
* @file oakcore_color_test.cpp
|
||||
* @brief Pure C API test for the OakColor ABI
|
||||
*
|
||||
* Exercises every function of oakcore/color.h through the C boundary only:
|
||||
* no C++ wrapper, no test framework, just main() + assert().
|
||||
*/
|
||||
|
||||
#include "olive/core/oakcore/color.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* PixelFormat::Format values (render/pixelformat.h) */
|
||||
enum { PF_U8 = 0, PF_U10 = 1, PF_U16 = 2, PF_F16 = 3, PF_F32 = 4 };
|
||||
|
||||
static int feq(float a, float b)
|
||||
{
|
||||
return fabsf(a - b) < 1e-5f;
|
||||
}
|
||||
|
||||
static void test_create_and_channels(void)
|
||||
{
|
||||
/* Default construction: all channels zero */
|
||||
OakColor *c = oakcore_color_create();
|
||||
assert(c != NULL);
|
||||
assert(feq(oakcore_color_red(c), 0.0f));
|
||||
assert(feq(oakcore_color_green(c), 0.0f));
|
||||
assert(feq(oakcore_color_blue(c), 0.0f));
|
||||
assert(feq(oakcore_color_alpha(c), 0.0f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* RGBA construction + getters */
|
||||
c = oakcore_color_create_rgba(0.25f, 0.5f, 0.75f, 1.0f);
|
||||
assert(feq(oakcore_color_red(c), 0.25f));
|
||||
assert(feq(oakcore_color_green(c), 0.5f));
|
||||
assert(feq(oakcore_color_blue(c), 0.75f));
|
||||
assert(feq(oakcore_color_alpha(c), 1.0f));
|
||||
|
||||
/* Setters, including out-of-gamut HDR values */
|
||||
oakcore_color_set_red(c, -0.5f);
|
||||
oakcore_color_set_green(c, 2.0f);
|
||||
oakcore_color_set_blue(c, 0.0f);
|
||||
oakcore_color_set_alpha(c, 0.125f);
|
||||
assert(feq(oakcore_color_red(c), -0.5f));
|
||||
assert(feq(oakcore_color_green(c), 2.0f));
|
||||
assert(feq(oakcore_color_blue(c), 0.0f));
|
||||
assert(feq(oakcore_color_alpha(c), 0.125f));
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_copy_and_ownership(void)
|
||||
{
|
||||
OakColor *c = oakcore_color_create_rgba(0.1f, 0.2f, 0.3f, 0.4f);
|
||||
OakColor *copy = oakcore_color_copy(c);
|
||||
assert(copy != NULL);
|
||||
assert(feq(oakcore_color_red(copy), 0.1f));
|
||||
assert(feq(oakcore_color_green(copy), 0.2f));
|
||||
assert(feq(oakcore_color_blue(copy), 0.3f));
|
||||
assert(feq(oakcore_color_alpha(copy), 0.4f));
|
||||
|
||||
/* The copy is independent from the original */
|
||||
oakcore_color_set_red(copy, 0.9f);
|
||||
assert(feq(oakcore_color_red(copy), 0.9f));
|
||||
assert(feq(oakcore_color_red(c), 0.1f));
|
||||
|
||||
oakcore_color_free(copy);
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* Releasing a NULL handle must be safe */
|
||||
oakcore_color_free(NULL);
|
||||
}
|
||||
|
||||
static void test_hsv(void)
|
||||
{
|
||||
/* Primary colors from HSV */
|
||||
OakColor *c = oakcore_color_from_hsv(0.0f, 1.0f, 1.0f);
|
||||
assert(feq(oakcore_color_red(c), 1.0f));
|
||||
assert(feq(oakcore_color_green(c), 0.0f));
|
||||
assert(feq(oakcore_color_blue(c), 0.0f));
|
||||
assert(feq(oakcore_color_alpha(c), 1.0f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
c = oakcore_color_from_hsv(120.0f, 1.0f, 1.0f);
|
||||
assert(feq(oakcore_color_red(c), 0.0f));
|
||||
assert(feq(oakcore_color_green(c), 1.0f));
|
||||
assert(feq(oakcore_color_blue(c), 0.0f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
c = oakcore_color_from_hsv(240.0f, 1.0f, 0.5f);
|
||||
assert(feq(oakcore_color_red(c), 0.0f));
|
||||
assert(feq(oakcore_color_green(c), 0.0f));
|
||||
assert(feq(oakcore_color_blue(c), 0.5f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* to_hsv of pure red and gray */
|
||||
c = oakcore_color_create_rgba(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
float h = -1.0f, s = -1.0f, v = -1.0f;
|
||||
oakcore_color_to_hsv(c, &h, &s, &v);
|
||||
assert(feq(h, 0.0f));
|
||||
assert(feq(s, 1.0f));
|
||||
assert(feq(v, 1.0f));
|
||||
assert(feq(oakcore_color_hsv_hue(c), h));
|
||||
assert(feq(oakcore_color_hsv_saturation(c), s));
|
||||
assert(feq(oakcore_color_value(c), v));
|
||||
oakcore_color_free(c);
|
||||
|
||||
c = oakcore_color_create_rgba(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
oakcore_color_to_hsv(c, &h, &s, &v);
|
||||
assert(feq(h, 0.0f));
|
||||
assert(feq(s, 0.0f));
|
||||
assert(feq(v, 0.5f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* Roundtrip: color -> hsv -> color */
|
||||
c = oakcore_color_create_rgba(0.2f, 0.4f, 0.8f, 1.0f);
|
||||
oakcore_color_to_hsv(c, &h, &s, &v);
|
||||
assert(feq(h, 220.0f));
|
||||
assert(feq(s, 0.75f));
|
||||
assert(feq(v, 0.8f));
|
||||
OakColor *rt = oakcore_color_from_hsv(h, s, v);
|
||||
assert(feq(oakcore_color_red(rt), 0.2f));
|
||||
assert(feq(oakcore_color_green(rt), 0.4f));
|
||||
assert(feq(oakcore_color_blue(rt), 0.8f));
|
||||
oakcore_color_free(rt);
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_hsl(void)
|
||||
{
|
||||
/* Pure red: l = 0.5, s = 1, h = 0 */
|
||||
OakColor *c = oakcore_color_create_rgba(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
float h = -1.0f, s = -1.0f, l = -1.0f;
|
||||
oakcore_color_to_hsl(c, &h, &s, &l);
|
||||
assert(feq(h, 0.0f));
|
||||
assert(feq(s, 1.0f));
|
||||
assert(feq(l, 0.5f));
|
||||
assert(feq(oakcore_color_hsl_hue(c), h));
|
||||
assert(feq(oakcore_color_hsl_saturation(c), s));
|
||||
assert(feq(oakcore_color_lightness(c), l));
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* Gray: zero saturation */
|
||||
c = oakcore_color_create_rgba(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
oakcore_color_to_hsl(c, &h, &s, &l);
|
||||
assert(feq(h, 0.0f));
|
||||
assert(feq(s, 0.0f));
|
||||
assert(feq(l, 0.5f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* Arbitrary color */
|
||||
c = oakcore_color_create_rgba(0.2f, 0.4f, 0.8f, 1.0f);
|
||||
oakcore_color_to_hsl(c, &h, &s, &l);
|
||||
assert(feq(h, 220.0f));
|
||||
assert(feq(s, 0.6f));
|
||||
assert(feq(l, 0.5f));
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_data_access(void)
|
||||
{
|
||||
OakColor *c = oakcore_color_create_rgba(0.1f, 0.2f, 0.3f, 0.4f);
|
||||
|
||||
/* Const read access */
|
||||
const float *cd = oakcore_color_const_data(c);
|
||||
assert(cd != NULL);
|
||||
assert(feq(cd[0], 0.1f));
|
||||
assert(feq(cd[1], 0.2f));
|
||||
assert(feq(cd[2], 0.3f));
|
||||
assert(feq(cd[3], 0.4f));
|
||||
|
||||
/* Mutable write access */
|
||||
float *d = oakcore_color_data(c);
|
||||
assert(d != NULL);
|
||||
d[0] = 0.125f;
|
||||
d[3] = 1.0f;
|
||||
assert(feq(oakcore_color_red(c), 0.125f));
|
||||
assert(feq(oakcore_color_alpha(c), 1.0f));
|
||||
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_pixel_data(void)
|
||||
{
|
||||
char buf[16];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
/* u8 roundtrip, 4 channels */
|
||||
OakColor *c = oakcore_color_create_rgba(1.0f, 0.0f, 1.0f, 1.0f);
|
||||
oakcore_color_to_data(c, buf, PF_U8, 4);
|
||||
assert((uint8_t)buf[0] == 255);
|
||||
assert((uint8_t)buf[1] == 0);
|
||||
assert((uint8_t)buf[2] == 255);
|
||||
assert((uint8_t)buf[3] == 255);
|
||||
OakColor *back = oakcore_color_from_data(buf, PF_U8, 4);
|
||||
assert(feq(oakcore_color_red(back), 1.0f));
|
||||
assert(feq(oakcore_color_green(back), 0.0f));
|
||||
assert(feq(oakcore_color_blue(back), 1.0f));
|
||||
assert(feq(oakcore_color_alpha(back), 1.0f));
|
||||
oakcore_color_free(back);
|
||||
|
||||
/* u8 with only 3 channels: alpha stays 0 */
|
||||
oakcore_color_to_data(c, buf, PF_U8, 3);
|
||||
back = oakcore_color_from_data(buf, PF_U8, 3);
|
||||
assert(feq(oakcore_color_red(back), 1.0f));
|
||||
assert(feq(oakcore_color_green(back), 0.0f));
|
||||
assert(feq(oakcore_color_blue(back), 1.0f));
|
||||
assert(feq(oakcore_color_alpha(back), 0.0f));
|
||||
oakcore_color_free(back);
|
||||
|
||||
/* u16 roundtrip */
|
||||
oakcore_color_to_data(c, buf, PF_U16, 4);
|
||||
assert(((uint16_t *)buf)[0] == 65535);
|
||||
assert(((uint16_t *)buf)[1] == 0);
|
||||
back = oakcore_color_from_data(buf, PF_U16, 4);
|
||||
assert(feq(oakcore_color_red(back), 1.0f));
|
||||
assert(feq(oakcore_color_green(back), 0.0f));
|
||||
assert(feq(oakcore_color_alpha(back), 1.0f));
|
||||
oakcore_color_free(back);
|
||||
oakcore_color_free(c);
|
||||
|
||||
/* f32 roundtrip, exact */
|
||||
c = oakcore_color_create_rgba(0.25f, 0.5f, 0.75f, 1.0f);
|
||||
oakcore_color_to_data(c, buf, PF_F32, 4);
|
||||
assert(feq(((float *)buf)[0], 0.25f));
|
||||
assert(feq(((float *)buf)[3], 1.0f));
|
||||
back = oakcore_color_from_data(buf, PF_F32, 4);
|
||||
assert(feq(oakcore_color_red(back), 0.25f));
|
||||
assert(feq(oakcore_color_green(back), 0.5f));
|
||||
assert(feq(oakcore_color_blue(back), 0.75f));
|
||||
assert(feq(oakcore_color_alpha(back), 1.0f));
|
||||
oakcore_color_free(back);
|
||||
|
||||
/* f16 roundtrip: 1.0 = 0x3C00, 0.5 = 0x3800 in IEEE half */
|
||||
c = oakcore_color_create_rgba(1.0f, 0.5f, 0.0f, 1.0f);
|
||||
oakcore_color_to_data(c, buf, PF_F16, 4);
|
||||
assert(((uint16_t *)buf)[0] == 0x3C00);
|
||||
assert(((uint16_t *)buf)[1] == 0x3800);
|
||||
back = oakcore_color_from_data(buf, PF_F16, 4);
|
||||
assert(feq(oakcore_color_red(back), 1.0f));
|
||||
assert(feq(oakcore_color_green(back), 0.5f));
|
||||
assert(feq(oakcore_color_blue(back), 0.0f));
|
||||
assert(feq(oakcore_color_alpha(back), 1.0f));
|
||||
oakcore_color_free(back);
|
||||
|
||||
/* u10 packed 4-channel roundtrip: all ones packs to 0xFFFFFFFF */
|
||||
c = oakcore_color_create_rgba(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
oakcore_color_to_data(c, buf, PF_U10, 4);
|
||||
assert(((uint32_t *)buf)[0] == 0xFFFFFFFFu);
|
||||
back = oakcore_color_from_data(buf, PF_U10, 4);
|
||||
assert(feq(oakcore_color_red(back), 1.0f));
|
||||
assert(feq(oakcore_color_green(back), 1.0f));
|
||||
assert(feq(oakcore_color_blue(back), 1.0f));
|
||||
assert(feq(oakcore_color_alpha(back), 1.0f));
|
||||
oakcore_color_free(back);
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_luminance(void)
|
||||
{
|
||||
/* (2r + b + 3g) / 6 */
|
||||
OakColor *c = oakcore_color_create_rgba(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
assert(feq(oakcore_color_get_rough_luminance(c), 1.0f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
c = oakcore_color_create_rgba(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
assert(feq(oakcore_color_get_rough_luminance(c), 0.5f));
|
||||
oakcore_color_free(c);
|
||||
|
||||
c = oakcore_color_create_rgba(0.6f, 0.2f, 0.4f, 1.0f);
|
||||
assert(feq(oakcore_color_get_rough_luminance(c),
|
||||
(2.0f * 0.6f + 0.4f + 3.0f * 0.2f) / 6.0f));
|
||||
oakcore_color_free(c);
|
||||
}
|
||||
|
||||
static void test_math(void)
|
||||
{
|
||||
OakColor *a = oakcore_color_create_rgba(0.1f, 0.2f, 0.3f, 0.4f);
|
||||
OakColor *b = oakcore_color_create_rgba(0.4f, 0.3f, 0.2f, 0.1f);
|
||||
|
||||
/* Color +=/-= Color */
|
||||
oakcore_color_add_assign(a, b);
|
||||
assert(feq(oakcore_color_red(a), 0.5f));
|
||||
assert(feq(oakcore_color_green(a), 0.5f));
|
||||
assert(feq(oakcore_color_blue(a), 0.5f));
|
||||
assert(feq(oakcore_color_alpha(a), 0.5f));
|
||||
oakcore_color_sub_assign(a, b);
|
||||
assert(feq(oakcore_color_red(a), 0.1f));
|
||||
assert(feq(oakcore_color_green(a), 0.2f));
|
||||
assert(feq(oakcore_color_blue(a), 0.3f));
|
||||
assert(feq(oakcore_color_alpha(a), 0.4f));
|
||||
|
||||
/* Scalar assign operators (apply to all channels) */
|
||||
oakcore_color_add_scalar_assign(a, 1.0f);
|
||||
assert(feq(oakcore_color_red(a), 1.1f));
|
||||
assert(feq(oakcore_color_alpha(a), 1.4f));
|
||||
oakcore_color_sub_scalar_assign(a, 0.6f);
|
||||
assert(feq(oakcore_color_red(a), 0.5f));
|
||||
assert(feq(oakcore_color_alpha(a), 0.8f));
|
||||
oakcore_color_mul_scalar_assign(a, 2.0f);
|
||||
assert(feq(oakcore_color_red(a), 1.0f));
|
||||
assert(feq(oakcore_color_green(a), 1.2f));
|
||||
assert(feq(oakcore_color_blue(a), 1.4f));
|
||||
assert(feq(oakcore_color_alpha(a), 1.6f));
|
||||
oakcore_color_div_scalar_assign(a, 4.0f);
|
||||
assert(feq(oakcore_color_red(a), 0.25f));
|
||||
assert(feq(oakcore_color_green(a), 0.3f));
|
||||
assert(feq(oakcore_color_blue(a), 0.35f));
|
||||
assert(feq(oakcore_color_alpha(a), 0.4f));
|
||||
|
||||
oakcore_color_free(b);
|
||||
oakcore_color_free(a);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_create_and_channels();
|
||||
test_copy_and_ownership();
|
||||
test_hsv();
|
||||
test_hsl();
|
||||
test_data_access();
|
||||
test_pixel_data();
|
||||
test_luminance();
|
||||
test_math();
|
||||
|
||||
printf("oakcore_color_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "oakcore/fractionutils.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
* @file oakcore_fractionutils_test.cpp
|
||||
* @brief Pure C API test for the fraction utility functions
|
||||
*
|
||||
* Exercises every oakcore_fractionutils_* function directly, without the
|
||||
* C++ wrapper and without a test framework.
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
// reduce_fraction: greatest common divisor is divided out
|
||||
{
|
||||
int64_t num = 6, den = 9;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, INT64_MAX);
|
||||
assert(num == 2 && den == 3);
|
||||
}
|
||||
|
||||
// reduce_fraction: the sign is normalized into the numerator
|
||||
{
|
||||
int64_t num = -6, den = 9;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, INT64_MAX);
|
||||
assert(num == -2 && den == 3);
|
||||
}
|
||||
{
|
||||
int64_t num = 6, den = -9;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, INT64_MAX);
|
||||
assert(num == -2 && den == 3);
|
||||
}
|
||||
|
||||
// reduce_fraction: a zero denominator is preserved, numerator zeroed
|
||||
{
|
||||
int64_t num = 42, den = 0;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, INT64_MAX);
|
||||
assert(num == 0 && den == 0);
|
||||
}
|
||||
|
||||
// reduce_fraction: a zero numerator reduces to 0/1
|
||||
{
|
||||
int64_t num = 0, den = 7;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, INT64_MAX);
|
||||
assert(num == 0 && den == 1);
|
||||
}
|
||||
|
||||
// reduce_fraction: exact reduction well within max
|
||||
{
|
||||
int64_t num = 1000000, den = 1000000;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, 100);
|
||||
assert(num == 1 && den == 1);
|
||||
}
|
||||
|
||||
// reduce_fraction: continued-fraction approximation respects max
|
||||
{
|
||||
int64_t num = 1048576, den = 1000000;
|
||||
oakcore_fractionutils_reduce_fraction(&num, &den, 10000);
|
||||
assert(num > 0 && num <= 10000 && den > 0 && den <= 10000);
|
||||
const double approx = double(num) / double(den);
|
||||
assert(fabs(approx - 1.048576) < 0.001);
|
||||
}
|
||||
|
||||
// compare_fractions: three-way result -1 / 0 / 1
|
||||
assert(oakcore_fractionutils_compare_fractions(1, 3, 1, 2) == -1);
|
||||
assert(oakcore_fractionutils_compare_fractions(1, 2, 2, 4) == 0);
|
||||
assert(oakcore_fractionutils_compare_fractions(2, 3, 1, 2) == 1);
|
||||
|
||||
// compare_fractions: meaningless degenerate comparison returns INT_MIN
|
||||
assert(oakcore_fractionutils_compare_fractions(0, 0, 0, 5) == INT_MIN);
|
||||
|
||||
// rescale_rnd: exact division needs no rounding
|
||||
assert(oakcore_fractionutils_rescale_rnd(100, 3, 4,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF) == 75);
|
||||
|
||||
// rescale_rnd: round to nearest, halfway cases away from zero
|
||||
assert(oakcore_fractionutils_rescale_rnd(5, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF) == 3);
|
||||
assert(oakcore_fractionutils_rescale_rnd(-5, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF) == -3);
|
||||
assert(oakcore_fractionutils_rescale_rnd(7, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF) == 4);
|
||||
|
||||
// rescale_rnd: round toward positive infinity
|
||||
assert(oakcore_fractionutils_rescale_rnd(5, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_UP) == 3);
|
||||
assert(oakcore_fractionutils_rescale_rnd(-5, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_UP) == -2);
|
||||
assert(oakcore_fractionutils_rescale_rnd(4, 1, 2,
|
||||
OAK_FRACTION_ROUNDING_UP) == 2);
|
||||
|
||||
// rescale_rnd: a negative divisor is normalized into the multiplier
|
||||
assert(oakcore_fractionutils_rescale_rnd(10, 1, -2,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF) == -5);
|
||||
|
||||
// rescale_rnd: large intermediates stay exact (128-bit path)
|
||||
assert(oakcore_fractionutils_rescale_rnd(INT64_MAX / 2, 4, 2,
|
||||
OAK_FRACTION_ROUNDING_NEAR_INF)
|
||||
== INT64_MAX - 1);
|
||||
|
||||
printf("oakcore_fractionutils_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "olive/core/oakcore/rational.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
// create_nd reduces and normalizes signs
|
||||
OakRational *r = oakcore_rational_create_nd(2, 4);
|
||||
assert(r != NULL);
|
||||
assert(oakcore_rational_numerator(r) == 1);
|
||||
assert(oakcore_rational_denominator(r) == 2);
|
||||
|
||||
OakRational *neg = oakcore_rational_create_nd(1, -2);
|
||||
assert(oakcore_rational_numerator(neg) == -1);
|
||||
assert(oakcore_rational_denominator(neg) == 2);
|
||||
oakcore_rational_free(neg);
|
||||
|
||||
// create defaults to n/1
|
||||
OakRational *five = oakcore_rational_create(5);
|
||||
assert(oakcore_rational_numerator(five) == 5);
|
||||
assert(oakcore_rational_denominator(five) == 1);
|
||||
oakcore_rational_free(five);
|
||||
|
||||
// NaN
|
||||
OakRational *nan = oakcore_rational_create_nan();
|
||||
assert(oakcore_rational_is_nan(nan) == 1);
|
||||
assert(oakcore_rational_is_null(nan) == 1);
|
||||
assert(isnan(oakcore_rational_to_double(nan)));
|
||||
oakcore_rational_free(nan);
|
||||
|
||||
// copy is deep
|
||||
OakRational *copy = oakcore_rational_copy(r);
|
||||
assert(copy != r);
|
||||
assert(oakcore_rational_compare(copy, r) == 0);
|
||||
oakcore_rational_add_assign(copy, r);
|
||||
assert(oakcore_rational_numerator(copy) == 1);
|
||||
assert(oakcore_rational_denominator(copy) == 1);
|
||||
assert(oakcore_rational_numerator(r) == 1);
|
||||
assert(oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(copy);
|
||||
|
||||
// to_double / to_string
|
||||
assert(fabs(oakcore_rational_to_double(r) - 0.5) < 1e-12);
|
||||
char buf[32];
|
||||
int needed = oakcore_rational_to_string(r, NULL, 0);
|
||||
assert(needed == 3);
|
||||
assert(oakcore_rational_to_string(r, buf, sizeof(buf)) == 3);
|
||||
assert(strcmp(buf, "1/2") == 0);
|
||||
assert(oakcore_rational_to_string(r, buf, 2) == 3); // truncated write
|
||||
assert(strcmp(buf, "1") == 0);
|
||||
|
||||
// from_double / from_string
|
||||
int ok = 0;
|
||||
OakRational *half = oakcore_rational_from_double(0.5, &ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_compare(half, r) == 0);
|
||||
oakcore_rational_free(half);
|
||||
|
||||
OakRational *bad = oakcore_rational_from_double(NAN, &ok);
|
||||
assert(ok == 0);
|
||||
assert(oakcore_rational_is_nan(bad));
|
||||
oakcore_rational_free(bad);
|
||||
|
||||
OakRational *parsed = oakcore_rational_from_string("3/4", &ok);
|
||||
assert(ok == 1);
|
||||
assert(fabs(oakcore_rational_to_double(parsed) - 0.75) < 1e-12);
|
||||
oakcore_rational_free(parsed);
|
||||
|
||||
OakRational *unparsed = oakcore_rational_from_string("1/2/3", &ok);
|
||||
assert(ok == 0);
|
||||
oakcore_rational_free(unparsed);
|
||||
|
||||
// flip
|
||||
OakRational *flip = oakcore_rational_flipped(r);
|
||||
assert(oakcore_rational_numerator(flip) == 2);
|
||||
assert(oakcore_rational_denominator(flip) == 1);
|
||||
oakcore_rational_flip(flip);
|
||||
assert(oakcore_rational_compare(flip, r) == 0);
|
||||
oakcore_rational_free(flip);
|
||||
|
||||
// arithmetic assignments
|
||||
OakRational *a = oakcore_rational_create_nd(1, 3);
|
||||
OakRational *b = oakcore_rational_create_nd(1, 6);
|
||||
oakcore_rational_add_assign(a, b);
|
||||
assert(oakcore_rational_numerator(a) == 1);
|
||||
assert(oakcore_rational_denominator(a) == 2);
|
||||
oakcore_rational_sub_assign(a, b);
|
||||
assert(oakcore_rational_numerator(a) == 1);
|
||||
assert(oakcore_rational_denominator(a) == 3);
|
||||
oakcore_rational_mul_assign(a, b);
|
||||
assert(oakcore_rational_numerator(a) == 1);
|
||||
assert(oakcore_rational_denominator(a) == 18);
|
||||
oakcore_rational_div_assign(a, b);
|
||||
assert(oakcore_rational_numerator(a) == 1);
|
||||
assert(oakcore_rational_denominator(a) == 3);
|
||||
|
||||
// compare ordering
|
||||
assert(oakcore_rational_compare(b, a) < 0);
|
||||
assert(oakcore_rational_compare(a, b) > 0);
|
||||
|
||||
oakcore_rational_free(a);
|
||||
oakcore_rational_free(b);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
printf("oakcore_rational_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "olive/core/oakcore/samplebuffer.h"
|
||||
|
||||
/* These functions are declared in oakcore/audioparams.h (a parallel
|
||||
* delivery); they are re-declared here so that this test only includes the
|
||||
* samplebuffer C header it is meant to exercise. */
|
||||
extern "C" {
|
||||
OakAudioParams *oakcore_audioparams_create(int sample_rate,
|
||||
uint64_t channel_layout,
|
||||
int format);
|
||||
void oakcore_audioparams_free(OakAudioParams *self);
|
||||
int oakcore_audioparams_sample_rate(const OakAudioParams *self);
|
||||
int oakcore_audioparams_channel_count(const OakAudioParams *self);
|
||||
}
|
||||
|
||||
/* Values mirror render/channellayout.h (k_channel_layout_stereo) and
|
||||
* render/sampleformat.h (SampleFormat::f32_p) on the library side. */
|
||||
#define SAMPLE_RATE 48000
|
||||
#define CHANNEL_LAYOUT_STEREO 0x3
|
||||
#define FORMAT_F32P 4
|
||||
|
||||
static int float_eq(float a, float b)
|
||||
{
|
||||
return fabsf(a - b) < 1e-6f;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
/* --- Default construction: empty, unallocated --- */
|
||||
OakSampleBuffer *def = oakcore_samplebuffer_create();
|
||||
assert(def != NULL);
|
||||
assert(oakcore_samplebuffer_is_allocated(def) == 0);
|
||||
assert(oakcore_samplebuffer_sample_count(def) == 0);
|
||||
assert(oakcore_samplebuffer_channel_count(def) == 0);
|
||||
assert(oakcore_samplebuffer_data(def, 0) == NULL);
|
||||
|
||||
/* audio_params() hands back an owned copy of the (invalid) defaults */
|
||||
OakAudioParams *def_params = oakcore_samplebuffer_audio_params(def);
|
||||
assert(def_params != NULL);
|
||||
assert(oakcore_audioparams_sample_rate(def_params) == 0);
|
||||
oakcore_audioparams_free(def_params);
|
||||
|
||||
/* Operations that need an allocation warn and leave the buffer alone */
|
||||
oakcore_samplebuffer_allocate(def);
|
||||
assert(oakcore_samplebuffer_is_allocated(def) == 0);
|
||||
oakcore_samplebuffer_silence(def);
|
||||
oakcore_samplebuffer_reverse(def);
|
||||
assert(oakcore_samplebuffer_is_allocated(def) == 0);
|
||||
oakcore_samplebuffer_free(def);
|
||||
|
||||
/* --- Construction from audio params + sample count --- */
|
||||
OakAudioParams *stereo = oakcore_audioparams_create(
|
||||
SAMPLE_RATE, CHANNEL_LAYOUT_STEREO, FORMAT_F32P);
|
||||
assert(stereo != NULL);
|
||||
|
||||
OakSampleBuffer *buf = oakcore_samplebuffer_create_samples(stereo, 100);
|
||||
assert(oakcore_samplebuffer_is_allocated(buf) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(buf) == 100);
|
||||
assert(oakcore_samplebuffer_channel_count(buf) == 2);
|
||||
|
||||
/* A fresh allocation is silent */
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
const float *d = oakcore_samplebuffer_data(buf, ch);
|
||||
assert(d != NULL);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assert(float_eq(d[i], 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
/* audio_params() copy reflects the construction parameters */
|
||||
OakAudioParams *p = oakcore_samplebuffer_audio_params(buf);
|
||||
assert(oakcore_audioparams_sample_rate(p) == SAMPLE_RATE);
|
||||
assert(oakcore_audioparams_channel_count(p) == 2);
|
||||
oakcore_audioparams_free(p);
|
||||
|
||||
/* data() rejects out-of-range channels */
|
||||
assert(oakcore_samplebuffer_data(buf, -1) == NULL);
|
||||
assert(oakcore_samplebuffer_data(buf, 2) == NULL);
|
||||
|
||||
/* --- Construction from audio params + rational length (0.5s) --- */
|
||||
OakRational *half_sec = oakcore_rational_create_nd(1, 2);
|
||||
OakSampleBuffer *timed =
|
||||
oakcore_samplebuffer_create_length(stereo, half_sec);
|
||||
assert(oakcore_samplebuffer_is_allocated(timed) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(timed) == 24000);
|
||||
oakcore_rational_free(half_sec);
|
||||
oakcore_samplebuffer_free(timed);
|
||||
|
||||
/* --- Manual lifecycle: params + count + allocate, destroy, repeat --- */
|
||||
OakSampleBuffer *manual = oakcore_samplebuffer_create();
|
||||
oakcore_samplebuffer_set_audio_params(manual, stereo);
|
||||
oakcore_samplebuffer_set_sample_count(manual, 50);
|
||||
oakcore_samplebuffer_allocate(manual);
|
||||
assert(oakcore_samplebuffer_is_allocated(manual) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(manual) == 50);
|
||||
assert(oakcore_samplebuffer_channel_count(manual) == 2);
|
||||
|
||||
oakcore_samplebuffer_destroy(manual);
|
||||
assert(oakcore_samplebuffer_is_allocated(manual) == 0);
|
||||
assert(oakcore_samplebuffer_channel_count(manual) == 0);
|
||||
assert(oakcore_samplebuffer_data(manual, 0) == NULL);
|
||||
|
||||
oakcore_samplebuffer_set_sample_count(manual, 30);
|
||||
oakcore_samplebuffer_allocate(manual);
|
||||
assert(oakcore_samplebuffer_is_allocated(manual) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(manual) == 30);
|
||||
oakcore_samplebuffer_free(manual);
|
||||
|
||||
/* set_sample_count via a rational length: 0.001s at 48kHz = 48 samples */
|
||||
OakSampleBuffer *manual2 = oakcore_samplebuffer_create();
|
||||
oakcore_samplebuffer_set_audio_params(manual2, stereo);
|
||||
OakRational *one_ms = oakcore_rational_create_nd(1, 1000);
|
||||
oakcore_samplebuffer_set_sample_count_length(manual2, one_ms);
|
||||
oakcore_rational_free(one_ms);
|
||||
oakcore_samplebuffer_allocate(manual2);
|
||||
assert(oakcore_samplebuffer_sample_count(manual2) == 48);
|
||||
oakcore_samplebuffer_free(manual2);
|
||||
|
||||
/* --- data() direct access, set(), to_raw_ptrs() --- */
|
||||
float *ch0 = oakcore_samplebuffer_data(buf, 0);
|
||||
float *ch1 = oakcore_samplebuffer_data(buf, 1);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ch0[i] = (float)i; /* ramp 0..99 on channel 0 */
|
||||
ch1[i] = (float)(i * 10); /* ramp 0..990 on channel 1 */
|
||||
}
|
||||
|
||||
const float ins[4] = { -1.0f, -2.0f, -3.0f, -4.0f };
|
||||
oakcore_samplebuffer_set(buf, 0, ins, 10, 4); /* write at offset 10 */
|
||||
for (int k = 0; k < 4; k++) {
|
||||
assert(float_eq(ch0[10 + k], ins[k]));
|
||||
}
|
||||
assert(float_eq(ch0[9], 9.0f)); /* neighbours untouched */
|
||||
assert(float_eq(ch0[14], 14.0f));
|
||||
|
||||
oakcore_samplebuffer_set(buf, 1, ins, 0, 4); /* write at offset 0 */
|
||||
for (int k = 0; k < 4; k++) {
|
||||
assert(float_eq(ch1[k], ins[k]));
|
||||
}
|
||||
for (int k = 0; k < 4; k++) { /* restore the channel 1 ramp */
|
||||
ch1[k] = (float)(k * 10);
|
||||
}
|
||||
|
||||
float *ptrs[2] = { NULL, NULL };
|
||||
oakcore_samplebuffer_to_raw_ptrs(buf, ptrs);
|
||||
assert(ptrs[0] == ch0);
|
||||
assert(ptrs[1] == ch1);
|
||||
ptrs[1][50] = 123.0f; /* writes through the raw pointer land in the buffer */
|
||||
assert(float_eq(oakcore_samplebuffer_data(buf, 1)[50], 123.0f));
|
||||
ch1[50] = 500.0f; /* restore ramp value (50 * 10) */
|
||||
|
||||
/* --- copy: an independent deep copy --- */
|
||||
OakSampleBuffer *cp = oakcore_samplebuffer_copy(buf);
|
||||
assert(oakcore_samplebuffer_is_allocated(cp) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(cp) == 100);
|
||||
assert(oakcore_samplebuffer_channel_count(cp) == 2);
|
||||
assert(float_eq(oakcore_samplebuffer_data(cp, 0)[9], 9.0f));
|
||||
|
||||
/* --- transform_volume (in place, all channels) --- */
|
||||
oakcore_samplebuffer_transform_volume(cp, 2.0f);
|
||||
assert(float_eq(oakcore_samplebuffer_data(cp, 0)[9], 18.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(cp, 1)[9], 180.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(buf, 0)[9], 9.0f)); /* source keeps its data */
|
||||
|
||||
/* --- transform_volume_for_channel (in place, one channel) --- */
|
||||
oakcore_samplebuffer_transform_volume_for_channel(cp, 1, 0.5f);
|
||||
assert(float_eq(oakcore_samplebuffer_data(cp, 1)[9], 90.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(cp, 0)[9], 18.0f)); /* other channel untouched */
|
||||
oakcore_samplebuffer_free(cp);
|
||||
|
||||
/* --- static-style transforms: input -> output --- */
|
||||
OakSampleBuffer *tin = oakcore_samplebuffer_create_samples(stereo, 10);
|
||||
OakSampleBuffer *tout = oakcore_samplebuffer_create_samples(stereo, 10);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
oakcore_samplebuffer_data(tin, 0)[i] = (float)i;
|
||||
oakcore_samplebuffer_data(tin, 1)[i] = (float)(100 + i);
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_transform_volume_to(0.5f, tin, tout);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(tout, 0)[i], (float)i * 0.5f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(tout, 1)[i],
|
||||
(float)(100 + i) * 0.5f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(tin, 0)[i], (float)i)); /* input unchanged */
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_silence(tout);
|
||||
oakcore_samplebuffer_transform_volume_for_channel_to(1, 2.0f, tin, tout);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(tout, 1)[i],
|
||||
(float)(100 + i) * 2.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(tout, 0)[i], 0.0f)); /* channel 0 untouched */
|
||||
}
|
||||
|
||||
/* --- per-sample volume transforms --- */
|
||||
oakcore_samplebuffer_transform_volume_for_sample(tin, 3, 10.0f);
|
||||
assert(float_eq(oakcore_samplebuffer_data(tin, 0)[3], 30.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(tin, 1)[3], 1030.0f));
|
||||
|
||||
oakcore_samplebuffer_transform_volume_for_sample_on_channel(tin, 4, 0,
|
||||
100.0f);
|
||||
assert(float_eq(oakcore_samplebuffer_data(tin, 0)[4], 400.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(tin, 1)[4], 104.0f)); /* channel 1 untouched */
|
||||
oakcore_samplebuffer_free(tin);
|
||||
oakcore_samplebuffer_free(tout);
|
||||
|
||||
/* --- clamp() limits every channel to [-1, 1] --- */
|
||||
OakSampleBuffer *cl = oakcore_samplebuffer_create_samples(stereo, 4);
|
||||
oakcore_samplebuffer_data(cl, 0)[0] = 2.5f;
|
||||
oakcore_samplebuffer_data(cl, 0)[1] = -2.5f;
|
||||
oakcore_samplebuffer_data(cl, 1)[0] = 42.0f;
|
||||
oakcore_samplebuffer_data(cl, 1)[1] = 0.25f;
|
||||
oakcore_samplebuffer_clamp(cl);
|
||||
assert(float_eq(oakcore_samplebuffer_data(cl, 0)[0], 1.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(cl, 0)[1], -1.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(cl, 1)[0], 1.0f));
|
||||
assert(float_eq(oakcore_samplebuffer_data(cl, 1)[1], 0.25f));
|
||||
oakcore_samplebuffer_free(cl);
|
||||
|
||||
/* --- silence / silence_range / silence_bytes --- */
|
||||
OakSampleBuffer *si = oakcore_samplebuffer_create_samples(stereo, 10);
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
oakcore_samplebuffer_data(si, ch)[i] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_silence_range(si, 2, 5); /* samples [2, 5) */
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
const float *d = oakcore_samplebuffer_data(si, ch);
|
||||
assert(float_eq(d[1], 1.0f));
|
||||
assert(float_eq(d[2], 0.0f));
|
||||
assert(float_eq(d[4], 0.0f));
|
||||
assert(float_eq(d[5], 1.0f));
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_silence_bytes(si, 0, 2 * sizeof(float)); /* samples [0, 2) */
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
const float *d = oakcore_samplebuffer_data(si, ch);
|
||||
assert(float_eq(d[0], 0.0f));
|
||||
assert(float_eq(d[1], 0.0f));
|
||||
assert(float_eq(d[5], 1.0f));
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_silence(si); /* everything */
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
const float *d = oakcore_samplebuffer_data(si, ch);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(float_eq(d[i], 0.0f));
|
||||
}
|
||||
}
|
||||
oakcore_samplebuffer_free(si);
|
||||
|
||||
/* --- reverse() flips every channel --- */
|
||||
OakSampleBuffer *rv = oakcore_samplebuffer_create_samples(stereo, 5);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
oakcore_samplebuffer_data(rv, 0)[i] = (float)i;
|
||||
oakcore_samplebuffer_data(rv, 1)[i] = (float)(10 * i);
|
||||
}
|
||||
oakcore_samplebuffer_reverse(rv);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(rv, 0)[i], (float)(4 - i)));
|
||||
assert(float_eq(oakcore_samplebuffer_data(rv, 1)[i],
|
||||
(float)(10 * (4 - i))));
|
||||
}
|
||||
oakcore_samplebuffer_free(rv);
|
||||
|
||||
/* --- speed(2.0) halves the sample count, sampling the ramp exactly --- */
|
||||
OakSampleBuffer *sp = oakcore_samplebuffer_create_samples(stereo, 100);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
oakcore_samplebuffer_data(sp, 0)[i] = (float)i;
|
||||
}
|
||||
oakcore_samplebuffer_speed(sp, 2.0);
|
||||
assert(oakcore_samplebuffer_sample_count(sp) == 50);
|
||||
for (int i = 0; i < 50; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(sp, 0)[i], (float)(2 * i)));
|
||||
}
|
||||
oakcore_samplebuffer_free(sp);
|
||||
|
||||
/* --- fast_set(): channel copy between buffers --- */
|
||||
OakSampleBuffer *fa = oakcore_samplebuffer_create_samples(stereo, 10);
|
||||
OakSampleBuffer *fb = oakcore_samplebuffer_create_samples(stereo, 10);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
oakcore_samplebuffer_data(fa, 0)[i] = (float)i;
|
||||
oakcore_samplebuffer_data(fa, 1)[i] = (float)(100 + i);
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_fast_set(fb, fa, 1, 0); /* fb channel 1 <- fa channel 0 */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(fb, 1)[i], (float)i));
|
||||
assert(float_eq(oakcore_samplebuffer_data(fb, 0)[i], 0.0f));
|
||||
}
|
||||
|
||||
oakcore_samplebuffer_fast_set(fb, fa, 0, -1); /* from == -1 mirrors to */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(fb, 0)[i], (float)i));
|
||||
}
|
||||
oakcore_samplebuffer_free(fa);
|
||||
oakcore_samplebuffer_free(fb);
|
||||
|
||||
/* --- rip_channel(): a new mono buffer with one channel's samples --- */
|
||||
OakSampleBuffer *rip = oakcore_samplebuffer_rip_channel(buf, 1);
|
||||
assert(oakcore_samplebuffer_is_allocated(rip) == 1);
|
||||
assert(oakcore_samplebuffer_channel_count(rip) == 1);
|
||||
assert(oakcore_samplebuffer_sample_count(rip) == 100);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assert(float_eq(oakcore_samplebuffer_data(rip, 0)[i], (float)(i * 10)));
|
||||
}
|
||||
OakAudioParams *rip_params = oakcore_samplebuffer_audio_params(rip);
|
||||
assert(oakcore_audioparams_sample_rate(rip_params) == SAMPLE_RATE);
|
||||
assert(oakcore_audioparams_channel_count(rip_params) == 1);
|
||||
oakcore_audioparams_free(rip_params);
|
||||
oakcore_samplebuffer_free(rip);
|
||||
|
||||
/* --- rip_channel_vector(): query size, partial copy, full copy --- */
|
||||
assert(oakcore_samplebuffer_rip_channel_vector(buf, 1, NULL, 0) == 100);
|
||||
|
||||
float partial[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
assert(oakcore_samplebuffer_rip_channel_vector(buf, 1, partial, 4) == 100);
|
||||
for (int k = 0; k < 4; k++) {
|
||||
assert(float_eq(partial[k], (float)(k * 10)));
|
||||
}
|
||||
|
||||
float full[100];
|
||||
assert(oakcore_samplebuffer_rip_channel_vector(buf, 1, full, 100) == 100);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
assert(float_eq(full[i], (float)(i * 10)));
|
||||
}
|
||||
|
||||
/* --- Ownership: everything released --- */
|
||||
oakcore_samplebuffer_free(buf);
|
||||
oakcore_audioparams_free(stereo);
|
||||
|
||||
printf("oakcore_samplebuffer_test: all assertions passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
/**
|
||||
* @file oakcore_stringutils_test.cpp
|
||||
* @brief Pure C API test for oakcore/stringutils.h
|
||||
*
|
||||
* Exercises every C function directly (no C++ wrapper, no test framework).
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "olive/core/oakcore/stringutils.h"
|
||||
|
||||
static void test_split()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// Basic split
|
||||
char **arr = oakcore_stringutils_split("a,b,c", ',', &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 3);
|
||||
assert(strcmp(arr[0], "a") == 0);
|
||||
assert(strcmp(arr[1], "b") == 0);
|
||||
assert(strcmp(arr[2], "c") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// No separator present: single element equal to the whole string
|
||||
arr = oakcore_stringutils_split("abc", ',', &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 1);
|
||||
assert(strcmp(arr[0], "abc") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// Trailing separator yields a trailing empty string
|
||||
arr = oakcore_stringutils_split("a,", ',', &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 2);
|
||||
assert(strcmp(arr[0], "a") == 0);
|
||||
assert(strcmp(arr[1], "") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// Empty string yields one empty string
|
||||
arr = oakcore_stringutils_split("", ',', &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 1);
|
||||
assert(strcmp(arr[0], "") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// NULL string behaves like an empty string
|
||||
arr = oakcore_stringutils_split(NULL, ',', &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 1);
|
||||
assert(strcmp(arr[0], "") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
}
|
||||
|
||||
static void test_split_regex()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// Split on runs of digits
|
||||
char **arr = oakcore_stringutils_split_regex("a1b22c", "[0-9]+", &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 3);
|
||||
assert(strcmp(arr[0], "a") == 0);
|
||||
assert(strcmp(arr[1], "b") == 0);
|
||||
assert(strcmp(arr[2], "c") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// Pattern that never matches yields the whole string
|
||||
arr = oakcore_stringutils_split_regex("abc", "[0-9]+", &count);
|
||||
assert(arr != NULL);
|
||||
assert(count == 1);
|
||||
assert(strcmp(arr[0], "abc") == 0);
|
||||
oakcore_stringutils_free_string_array(arr, count);
|
||||
|
||||
// Freeing NULL is safe
|
||||
oakcore_stringutils_free_string_array(NULL, 0);
|
||||
}
|
||||
|
||||
static void test_to_int()
|
||||
{
|
||||
int ok = 0;
|
||||
|
||||
// Base 10
|
||||
assert(oakcore_stringutils_to_int("42", 10, &ok) == 42);
|
||||
assert(ok == 1);
|
||||
|
||||
// Negative
|
||||
assert(oakcore_stringutils_to_int("-17", 10, &ok) == -17);
|
||||
assert(ok == 1);
|
||||
|
||||
// Base 16
|
||||
assert(oakcore_stringutils_to_int("ff", 16, &ok) == 255);
|
||||
assert(ok == 1);
|
||||
|
||||
// Parser error: returns 0 and reports failure
|
||||
assert(oakcore_stringutils_to_int("xyz", 10, &ok) == 0);
|
||||
assert(ok == 0);
|
||||
|
||||
// ok output parameter is optional
|
||||
assert(oakcore_stringutils_to_int("7", 10, NULL) == 7);
|
||||
}
|
||||
|
||||
/* Variadic forwarder to exercise oakcore_stringutils_format_v() */
|
||||
static int format_forward(char *buf, int buf_size, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const int r = oakcore_stringutils_format_v(buf, buf_size, fmt, args);
|
||||
va_end(args);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void test_format()
|
||||
{
|
||||
char buf[64];
|
||||
|
||||
// Format with mixed argument types
|
||||
const int needed = oakcore_stringutils_format(buf, sizeof(buf), "%d-%s-%.2f",
|
||||
42, "mid", 1.5);
|
||||
assert(needed == 11);
|
||||
assert(strcmp(buf, "42-mid-1.50") == 0);
|
||||
|
||||
// NULL buffer queries the required size
|
||||
assert(oakcore_stringutils_format(NULL, 0, "%d-%s-%.2f", 42, "mid",
|
||||
1.5) == needed);
|
||||
|
||||
// Too-small buffer truncates but still reports the full required size
|
||||
char small[5];
|
||||
const int needed2 =
|
||||
oakcore_stringutils_format(small, sizeof(small), "%s", "abcdefgh");
|
||||
assert(needed2 == 8);
|
||||
assert(strcmp(small, "abcd") == 0);
|
||||
|
||||
// va_list form produces identical results
|
||||
char buf2[64];
|
||||
const int needed3 = format_forward(buf2, sizeof(buf2), "%d-%s-%.2f", 42,
|
||||
"mid", 1.5);
|
||||
assert(needed3 == needed);
|
||||
assert(strcmp(buf2, buf) == 0);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
test_split();
|
||||
test_split_regex();
|
||||
test_to_int();
|
||||
test_format();
|
||||
|
||||
printf("oakcore_stringutils_test: all tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "olive/core/oakcore/timecodefunctions.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
char buf[64];
|
||||
int ok = 0;
|
||||
OakRational *r = NULL;
|
||||
|
||||
/* Common timebases: 30 fps non-drop, 29.97 drop-frame, 60 fps */
|
||||
OakRational *tb_30 = oakcore_rational_create_nd(1, 30);
|
||||
OakRational *tb_df = oakcore_rational_create_nd(1001, 30000);
|
||||
OakRational *tb_60 = oakcore_rational_create_nd(1, 60);
|
||||
|
||||
/* oakcore_timecode_time_to_timecode: drop-frame ( Olive's own test case ) */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create(1);
|
||||
const int size = oakcore_timecode_time_to_timecode(
|
||||
time, tb_df, OAK_TIMECODE_DISPLAY_DROP_FRAME, 0, NULL, 0);
|
||||
assert(size == 11);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_df, OAK_TIMECODE_DISPLAY_DROP_FRAME, 0, buf,
|
||||
sizeof(buf)) == 11);
|
||||
assert(strcmp(buf, "00:00:01;00") == 0);
|
||||
/* Truncating buffer still reports the full required size */
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_df, OAK_TIMECODE_DISPLAY_DROP_FRAME, 0, buf,
|
||||
6) == 11);
|
||||
assert(strcmp(buf, "00:00") == 0);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_time_to_timecode: invalid timebase */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create(0);
|
||||
OakRational *bizarre = oakcore_rational_create(156632219);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, bizarre, OAK_TIMECODE_DISPLAY_DROP_FRAME, 0, buf,
|
||||
sizeof(buf)) == 16);
|
||||
assert(strcmp(buf, "INVALID TIMEBASE") == 0);
|
||||
oakcore_rational_free(bizarre);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_time_to_timecode: non-drop, plus sign, negative */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create_nd(3, 2);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_NON_DROP_FRAME, 0, buf,
|
||||
sizeof(buf)) == 11);
|
||||
assert(strcmp(buf, "00:00:01:15") == 0);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_NON_DROP_FRAME, 1, buf,
|
||||
sizeof(buf)) == 12);
|
||||
assert(strcmp(buf, "+00:00:01:15") == 0);
|
||||
oakcore_rational_free(time);
|
||||
time = oakcore_rational_create_nd(-3, 2);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_NON_DROP_FRAME, 0, buf,
|
||||
sizeof(buf)) == 12);
|
||||
assert(strcmp(buf, "-00:00:01:15") == 0);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_time_to_timecode: seconds / frames / milliseconds */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create_nd(123, 2); /* 61.5 s */
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_SECONDS, 0, buf,
|
||||
sizeof(buf)) == 12);
|
||||
assert(strcmp(buf, "00:01:01.500") == 0);
|
||||
oakcore_rational_free(time);
|
||||
time = oakcore_rational_create_nd(3, 2);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_FRAMES, 0, buf,
|
||||
sizeof(buf)) == 2);
|
||||
assert(strcmp(buf, "45") == 0);
|
||||
assert(oakcore_timecode_time_to_timecode(
|
||||
time, tb_30, OAK_TIMECODE_DISPLAY_MILLISECONDS, 0, buf,
|
||||
sizeof(buf)) == 4);
|
||||
assert(strcmp(buf, "1500") == 0);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_timecode_to_time: non-drop */
|
||||
r = oakcore_timecode_timecode_to_time("00:00:01:15", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_NON_DROP_FRAME,
|
||||
&ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
/* oakcore_timecode_timecode_to_time: drop-frame */
|
||||
r = oakcore_timecode_timecode_to_time("00:00:01;00", tb_df,
|
||||
OAK_TIMECODE_DISPLAY_DROP_FRAME,
|
||||
&ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_numerator(r) == 1001 &&
|
||||
oakcore_rational_denominator(r) == 1000);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
/* oakcore_timecode_timecode_to_time: seconds / frames / milliseconds */
|
||||
r = oakcore_timecode_timecode_to_time("00:00:01.5", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_SECONDS, &ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
r = oakcore_timecode_timecode_to_time("45", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_FRAMES, &ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
r = oakcore_timecode_timecode_to_time("1500", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_MILLISECONDS,
|
||||
&ok);
|
||||
assert(ok == 1);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
/* oakcore_timecode_timecode_to_time: invalid and NULL input */
|
||||
r = oakcore_timecode_timecode_to_time("abc", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_NON_DROP_FRAME,
|
||||
&ok);
|
||||
assert(ok == 0);
|
||||
assert(oakcore_rational_numerator(r) == 0);
|
||||
oakcore_rational_free(r);
|
||||
r = oakcore_timecode_timecode_to_time(NULL, tb_30,
|
||||
OAK_TIMECODE_DISPLAY_NON_DROP_FRAME,
|
||||
&ok);
|
||||
assert(ok == 0);
|
||||
oakcore_rational_free(r);
|
||||
/* ok pointer itself may be NULL */
|
||||
r = oakcore_timecode_timecode_to_time("45", tb_30,
|
||||
OAK_TIMECODE_DISPLAY_FRAMES, NULL);
|
||||
assert(oakcore_rational_to_double(r) == 1.5);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
/* oakcore_timecode_time_to_string */
|
||||
assert(oakcore_timecode_time_to_string(3661000, NULL, 0) == 8);
|
||||
assert(oakcore_timecode_time_to_string(3661000, buf, sizeof(buf)) == 8);
|
||||
assert(strcmp(buf, "01:01:01") == 0);
|
||||
assert(oakcore_timecode_time_to_string(0, buf, sizeof(buf)) == 8);
|
||||
assert(strcmp(buf, "00:00:00") == 0);
|
||||
|
||||
/* oakcore_timecode_snap_time_to_timebase */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create_nd(102, 100); /* 1.02 s */
|
||||
r = oakcore_timecode_snap_time_to_timebase(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_ROUND);
|
||||
assert(oakcore_rational_numerator(r) == 31 &&
|
||||
oakcore_rational_denominator(r) == 30);
|
||||
oakcore_rational_free(r);
|
||||
oakcore_rational_free(time);
|
||||
time = oakcore_rational_create_nd(151, 100); /* 1.51 s -> 45.3 frames */
|
||||
r = oakcore_timecode_snap_time_to_timebase(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_FLOOR);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
r = oakcore_timecode_snap_time_to_timebase(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_CEIL);
|
||||
assert(oakcore_rational_numerator(r) == 23 &&
|
||||
oakcore_rational_denominator(r) == 15);
|
||||
oakcore_rational_free(r);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_time_to_timestamp (Rational) */
|
||||
{
|
||||
OakRational *time = oakcore_rational_create_nd(3, 2);
|
||||
assert(oakcore_timecode_time_to_timestamp(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_ROUND) == 45);
|
||||
oakcore_rational_free(time);
|
||||
time = oakcore_rational_create_nd(151, 100); /* 45.3 frames */
|
||||
assert(oakcore_timecode_time_to_timestamp(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_ROUND) == 45);
|
||||
assert(oakcore_timecode_time_to_timestamp(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_FLOOR) == 45);
|
||||
assert(oakcore_timecode_time_to_timestamp(
|
||||
time, tb_30, OAK_TIMECODE_ROUNDING_CEIL) == 46);
|
||||
oakcore_rational_free(time);
|
||||
}
|
||||
|
||||
/* oakcore_timecode_time_to_timestamp_d (double) */
|
||||
assert(oakcore_timecode_time_to_timestamp_d(1.5, tb_30,
|
||||
OAK_TIMECODE_ROUNDING_ROUND) ==
|
||||
45);
|
||||
assert(oakcore_timecode_time_to_timestamp_d(NAN, tb_30,
|
||||
OAK_TIMECODE_ROUNDING_ROUND) ==
|
||||
0);
|
||||
|
||||
/* oakcore_timecode_rescale_timestamp */
|
||||
assert(oakcore_timecode_rescale_timestamp(30, tb_30, tb_60) == 60);
|
||||
assert(oakcore_timecode_rescale_timestamp(30, tb_30, tb_30) == 30);
|
||||
{
|
||||
/* 1 * (1*4) / (5*3) = 0.2666... -> nearest is 0 */
|
||||
OakRational *src = oakcore_rational_create_nd(1, 5);
|
||||
OakRational *dst = oakcore_rational_create_nd(3, 4);
|
||||
assert(oakcore_timecode_rescale_timestamp(1, src, dst) == 0);
|
||||
/* ... but ceil rounds up to 1 */
|
||||
assert(oakcore_timecode_rescale_timestamp_ceil(1, src, dst) == 1);
|
||||
oakcore_rational_free(dst);
|
||||
oakcore_rational_free(src);
|
||||
}
|
||||
assert(oakcore_timecode_rescale_timestamp_ceil(30, tb_30, tb_60) == 60);
|
||||
|
||||
/* oakcore_timecode_timestamp_to_time */
|
||||
r = oakcore_timecode_timestamp_to_time(45, tb_30);
|
||||
assert(oakcore_rational_numerator(r) == 3 &&
|
||||
oakcore_rational_denominator(r) == 2);
|
||||
oakcore_rational_free(r);
|
||||
r = oakcore_timecode_timestamp_to_time(0, tb_30);
|
||||
assert(oakcore_rational_to_double(r) == 0.0);
|
||||
oakcore_rational_free(r);
|
||||
|
||||
/* oakcore_timecode_timebase_is_drop_frame */
|
||||
assert(oakcore_timecode_timebase_is_drop_frame(tb_df) == 1);
|
||||
assert(oakcore_timecode_timebase_is_drop_frame(tb_30) == 0);
|
||||
|
||||
oakcore_rational_free(tb_60);
|
||||
oakcore_rational_free(tb_df);
|
||||
oakcore_rational_free(tb_30);
|
||||
|
||||
printf("oakcore_timecodefunctions_test: all tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
/***
|
||||
|
||||
Oak - Non-Linear Video Editor
|
||||
Copyright (C) 2026 Oak Team
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
/**
|
||||
* @file oakcore_timerange_test.cpp
|
||||
* @brief Smoke test for the OakTimeRange C ABI
|
||||
*
|
||||
* Pure C API test: no gtest, no C++ wrapper classes. Every allocated handle
|
||||
* is released with the matching free function.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "olive/core/oakcore/timerange.h"
|
||||
|
||||
static OakRational *mk_time(int n)
|
||||
{
|
||||
return oakcore_rational_create(n);
|
||||
}
|
||||
|
||||
static OakTimeRange *mk_range(int in, int out)
|
||||
{
|
||||
OakRational *ri = mk_time(in);
|
||||
OakRational *ro = mk_time(out);
|
||||
OakTimeRange *r = oakcore_timerange_create_io(ri, ro);
|
||||
oakcore_rational_free(ri);
|
||||
oakcore_rational_free(ro);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void expect_int_time(const OakRational *r, int expected)
|
||||
{
|
||||
assert(oakcore_rational_numerator(r) == expected);
|
||||
assert(oakcore_rational_denominator(r) == 1);
|
||||
}
|
||||
|
||||
static void expect_range(const OakTimeRange *r, int in, int out)
|
||||
{
|
||||
OakRational *ri = oakcore_timerange_in(r);
|
||||
OakRational *ro = oakcore_timerange_out(r);
|
||||
expect_int_time(ri, in);
|
||||
expect_int_time(ro, out);
|
||||
oakcore_rational_free(ri);
|
||||
oakcore_rational_free(ro);
|
||||
}
|
||||
|
||||
static void test_create_default(void)
|
||||
{
|
||||
OakTimeRange *r = oakcore_timerange_create();
|
||||
expect_range(r, 0, 0);
|
||||
|
||||
OakRational *length = oakcore_timerange_length(r);
|
||||
expect_int_time(length, 0);
|
||||
oakcore_rational_free(length);
|
||||
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_create_and_getters(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(10, 20);
|
||||
expect_range(r, 10, 20);
|
||||
|
||||
OakRational *length = oakcore_timerange_length(r);
|
||||
expect_int_time(length, 10);
|
||||
oakcore_rational_free(length);
|
||||
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_normalize_swaps_in_out(void)
|
||||
{
|
||||
// out earlier than in must be swapped
|
||||
OakTimeRange *r = mk_range(20, 10);
|
||||
expect_range(r, 10, 20);
|
||||
|
||||
OakRational *length = oakcore_timerange_length(r);
|
||||
expect_int_time(length, 10);
|
||||
oakcore_rational_free(length);
|
||||
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_length_nan_on_extremes(void)
|
||||
{
|
||||
// RATIONAL_MIN/RATIONAL_MAX endpoints make the length NaN
|
||||
OakTimeRange *r = mk_range(INT_MIN, 0);
|
||||
OakRational *length = oakcore_timerange_length(r);
|
||||
assert(oakcore_rational_is_nan(length) == 1);
|
||||
oakcore_rational_free(length);
|
||||
oakcore_timerange_free(r);
|
||||
|
||||
r = mk_range(0, INT_MAX);
|
||||
length = oakcore_timerange_length(r);
|
||||
assert(oakcore_rational_is_nan(length) == 1);
|
||||
oakcore_rational_free(length);
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_copy_and_equal(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(3, 7);
|
||||
OakTimeRange *copy = oakcore_timerange_copy(r);
|
||||
|
||||
assert(oakcore_timerange_equal(r, copy) == 1);
|
||||
expect_range(copy, 3, 7);
|
||||
|
||||
// Mutating the copy must not affect the original (deep ownership)
|
||||
OakRational *nine = mk_time(9);
|
||||
oakcore_timerange_set_in(copy, nine);
|
||||
oakcore_rational_free(nine);
|
||||
|
||||
// set_in(9) with out==7 normalizes by swapping to (7, 9)
|
||||
expect_range(copy, 7, 9);
|
||||
assert(oakcore_timerange_equal(r, copy) == 0);
|
||||
expect_range(r, 3, 7);
|
||||
|
||||
oakcore_timerange_free(copy);
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_setters(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(0, 10);
|
||||
OakRational *t = mk_time(5);
|
||||
|
||||
oakcore_timerange_set_in(r, t);
|
||||
expect_range(r, 5, 10);
|
||||
|
||||
oakcore_timerange_set_out(r, t);
|
||||
// set_out(5) with in==5 collapses to a zero-length range
|
||||
expect_range(r, 5, 5);
|
||||
|
||||
{
|
||||
OakRational *in = mk_time(30);
|
||||
OakRational *out = mk_time(20);
|
||||
oakcore_timerange_set_range(r, in, out);
|
||||
oakcore_rational_free(in);
|
||||
oakcore_rational_free(out);
|
||||
}
|
||||
// set_range also normalizes
|
||||
expect_range(r, 20, 30);
|
||||
|
||||
oakcore_rational_free(t);
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_overlaps_with(void)
|
||||
{
|
||||
OakTimeRange *a = mk_range(0, 10);
|
||||
OakTimeRange *b = mk_range(10, 20);
|
||||
OakTimeRange *c = mk_range(5, 15);
|
||||
|
||||
// Touching ranges (0,10) vs (10,20): in_inclusive governs the
|
||||
// other.out-vs-self.in comparison, out_inclusive the other.in-vs-self.out
|
||||
// one, so the results are asymmetric for mixed flags
|
||||
assert(oakcore_timerange_overlaps_with(a, b, 1, 1) == 1);
|
||||
assert(oakcore_timerange_overlaps_with(a, b, 0, 0) == 0);
|
||||
assert(oakcore_timerange_overlaps_with(a, b, 1, 0) == 0);
|
||||
assert(oakcore_timerange_overlaps_with(a, b, 0, 1) == 1);
|
||||
|
||||
// Genuinely overlapping ranges overlap under any inclusivity
|
||||
assert(oakcore_timerange_overlaps_with(a, c, 1, 1) == 1);
|
||||
assert(oakcore_timerange_overlaps_with(a, c, 0, 0) == 1);
|
||||
|
||||
oakcore_timerange_free(c);
|
||||
oakcore_timerange_free(b);
|
||||
oakcore_timerange_free(a);
|
||||
}
|
||||
|
||||
static void test_contains_range(void)
|
||||
{
|
||||
OakTimeRange *outer = mk_range(0, 30);
|
||||
OakTimeRange *inner = mk_range(5, 10);
|
||||
OakTimeRange *same = mk_range(0, 30);
|
||||
OakTimeRange *partial = mk_range(5, 40);
|
||||
|
||||
assert(oakcore_timerange_contains_range(outer, inner, 1, 1) == 1);
|
||||
assert(oakcore_timerange_contains_range(outer, inner, 0, 0) == 1);
|
||||
|
||||
// Identical ranges: contained inclusively, not exclusively
|
||||
assert(oakcore_timerange_contains_range(outer, same, 1, 1) == 1);
|
||||
assert(oakcore_timerange_contains_range(outer, same, 0, 0) == 0);
|
||||
|
||||
assert(oakcore_timerange_contains_range(outer, partial, 1, 1) == 0);
|
||||
assert(oakcore_timerange_contains_range(inner, outer, 1, 1) == 0);
|
||||
|
||||
oakcore_timerange_free(partial);
|
||||
oakcore_timerange_free(same);
|
||||
oakcore_timerange_free(inner);
|
||||
oakcore_timerange_free(outer);
|
||||
}
|
||||
|
||||
static void test_contains_time(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(0, 10);
|
||||
OakRational *inside = mk_time(5);
|
||||
OakRational *edge_in = mk_time(0);
|
||||
OakRational *edge_out = mk_time(10);
|
||||
OakRational *outside = mk_time(-1);
|
||||
|
||||
// contains(time) is in-inclusive but out-exclusive
|
||||
assert(oakcore_timerange_contains_time(r, inside) == 1);
|
||||
assert(oakcore_timerange_contains_time(r, edge_in) == 1);
|
||||
assert(oakcore_timerange_contains_time(r, edge_out) == 0);
|
||||
assert(oakcore_timerange_contains_time(r, outside) == 0);
|
||||
|
||||
oakcore_rational_free(outside);
|
||||
oakcore_rational_free(edge_out);
|
||||
oakcore_rational_free(edge_in);
|
||||
oakcore_rational_free(inside);
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_combine_and_intersect(void)
|
||||
{
|
||||
OakTimeRange *a = mk_range(0, 10);
|
||||
OakTimeRange *b = mk_range(20, 30);
|
||||
|
||||
OakTimeRange *combined = oakcore_timerange_combined(a, b);
|
||||
expect_range(combined, 0, 30);
|
||||
oakcore_timerange_free(combined);
|
||||
|
||||
combined = oakcore_timerange_combine(b, a);
|
||||
expect_range(combined, 0, 30);
|
||||
oakcore_timerange_free(combined);
|
||||
|
||||
// Disjoint ranges intersect to (20, 10), which normalize() swaps to (10, 20)
|
||||
OakTimeRange *intersected = oakcore_timerange_intersected(a, b);
|
||||
expect_range(intersected, 10, 20);
|
||||
oakcore_timerange_free(intersected);
|
||||
|
||||
oakcore_timerange_free(b);
|
||||
oakcore_timerange_free(a);
|
||||
|
||||
a = mk_range(0, 20);
|
||||
b = mk_range(10, 30);
|
||||
|
||||
intersected = oakcore_timerange_intersected(a, b);
|
||||
expect_range(intersected, 10, 20);
|
||||
oakcore_timerange_free(intersected);
|
||||
|
||||
intersected = oakcore_timerange_intersect(b, a);
|
||||
expect_range(intersected, 10, 20);
|
||||
oakcore_timerange_free(intersected);
|
||||
|
||||
oakcore_timerange_free(b);
|
||||
oakcore_timerange_free(a);
|
||||
}
|
||||
|
||||
static void test_arithmetic(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(0, 10);
|
||||
OakRational *five = mk_time(5);
|
||||
|
||||
OakTimeRange *shifted = oakcore_timerange_add(r, five);
|
||||
expect_range(shifted, 5, 15);
|
||||
|
||||
OakTimeRange *back = oakcore_timerange_sub(shifted, five);
|
||||
expect_range(back, 0, 10);
|
||||
oakcore_timerange_free(back);
|
||||
oakcore_timerange_free(shifted);
|
||||
|
||||
oakcore_timerange_add_assign(r, five);
|
||||
expect_range(r, 5, 15);
|
||||
|
||||
oakcore_timerange_sub_assign(r, five);
|
||||
expect_range(r, 0, 10);
|
||||
|
||||
OakRational *length = oakcore_timerange_length(r);
|
||||
expect_int_time(length, 10);
|
||||
oakcore_rational_free(length);
|
||||
|
||||
oakcore_rational_free(five);
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
static void test_split(void)
|
||||
{
|
||||
OakTimeRange *r = mk_range(0, 10);
|
||||
|
||||
// Size query, both directly and through the NULL-buffer form
|
||||
assert(oakcore_timerange_split_count(r, 4) == 3);
|
||||
assert(oakcore_timerange_split(r, 4, NULL, 0) == 3);
|
||||
|
||||
// Full split: (0,4), (4,8), (8,10)
|
||||
OakTimeRange *chunks[3];
|
||||
assert(oakcore_timerange_split(r, 4, chunks, 3) == 3);
|
||||
expect_range(chunks[0], 0, 4);
|
||||
expect_range(chunks[1], 4, 8);
|
||||
expect_range(chunks[2], 8, 10);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
oakcore_timerange_free(chunks[i]);
|
||||
}
|
||||
|
||||
// A too-small buffer receives a prefix, the return value is the total
|
||||
OakTimeRange *prefix[2];
|
||||
assert(oakcore_timerange_split(r, 4, prefix, 2) == 3);
|
||||
expect_range(prefix[0], 0, 4);
|
||||
expect_range(prefix[1], 4, 8);
|
||||
oakcore_timerange_free(prefix[0]);
|
||||
oakcore_timerange_free(prefix[1]);
|
||||
|
||||
// Zero-length range splits into exactly one chunk
|
||||
OakTimeRange *zero = mk_range(5, 5);
|
||||
assert(oakcore_timerange_split_count(zero, 4) == 1);
|
||||
OakTimeRange *single = NULL;
|
||||
assert(oakcore_timerange_split(zero, 4, &single, 1) == 1);
|
||||
expect_range(single, 5, 5);
|
||||
oakcore_timerange_free(single);
|
||||
oakcore_timerange_free(zero);
|
||||
|
||||
oakcore_timerange_free(r);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_create_default();
|
||||
test_create_and_getters();
|
||||
test_normalize_swaps_in_out();
|
||||
test_length_nan_on_extremes();
|
||||
test_copy_and_equal();
|
||||
test_setters();
|
||||
test_overlaps_with();
|
||||
test_contains_range();
|
||||
test_contains_time();
|
||||
test_combine_and_intersect();
|
||||
test_arithmetic();
|
||||
test_split();
|
||||
|
||||
printf("oakcore_timerange_test: all tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "util/rational.h"
|
||||
#include "util/tests.h"
|
||||
|
||||
using namespace olive::core;
|
||||
|
||||
bool rational_to_from_string_test()
|
||||
{
|
||||
Rational r(1, 30);
|
||||
|
||||
std::string s = r.toString();
|
||||
|
||||
Rational r2 = Rational::fromString(s);
|
||||
|
||||
return r == r2;
|
||||
}
|
||||
|
||||
bool rational_to_from_string_test2()
|
||||
{
|
||||
Rational r(69, 420);
|
||||
|
||||
std::string s = r.toString();
|
||||
|
||||
Rational r2 = Rational::fromString(s);
|
||||
|
||||
return r == r2;
|
||||
}
|
||||
|
||||
bool rational_defaults()
|
||||
{
|
||||
// By default, rationals are valid 0/1
|
||||
Rational basic_constructor;
|
||||
|
||||
if (!basic_constructor.isNull()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (basic_constructor.isNaN()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rational_nan()
|
||||
{
|
||||
// Create a NaN with a 0 denominator
|
||||
Rational nan = Rational(0, 0);
|
||||
if (!nan.isNaN())
|
||||
return false;
|
||||
if (!nan.isNull())
|
||||
return false;
|
||||
|
||||
// Create a non-NaN with a zero numerator
|
||||
Rational zero_nonnan(0, 999);
|
||||
if (!zero_nonnan.isNull())
|
||||
return false;
|
||||
if (zero_nonnan.isNaN())
|
||||
return false;
|
||||
|
||||
// Create a non-NaN with a non-zero numerator
|
||||
Rational nonzer_nonnan(1, 30);
|
||||
if (nonzer_nonnan.isNull())
|
||||
return false;
|
||||
if (nonzer_nonnan.isNaN())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rational_nan_constant()
|
||||
{
|
||||
return Rational::NaN.isNaN();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Tester t;
|
||||
|
||||
t.add("Rational::defaults", rational_defaults);
|
||||
t.add("Rational::NaN", rational_nan);
|
||||
t.add("Rational::NaN_constant", rational_nan_constant);
|
||||
t.add("Rational::toString/fromString", rational_to_from_string_test);
|
||||
t.add("Rational::toString/fromString2", rational_to_from_string_test2);
|
||||
|
||||
return t.exec();
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "util/stringutils.h"
|
||||
#include "util/tests.h"
|
||||
|
||||
using namespace olive::core;
|
||||
|
||||
bool stringutils_format_test()
|
||||
{
|
||||
const char *expected = "Hello, world!";
|
||||
std::string f = StringUtils::format("%s, %s!", "Hello", "world");
|
||||
if (strcmp(f.c_str(), expected) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Tester t;
|
||||
|
||||
t.add("StringUtils::format", stringutils_format_test);
|
||||
|
||||
return t.exec();
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "util/timecodefunctions.h"
|
||||
#include "util/tests.h"
|
||||
|
||||
using namespace olive::core;
|
||||
|
||||
bool timecodefunctions_time_to_timecode_test()
|
||||
{
|
||||
Rational drop_frame_30(1001, 30000);
|
||||
|
||||
std::string timecode = Timecode::time_to_timecode(
|
||||
Rational(1), drop_frame_30, Timecode::kTimecodeDropFrame);
|
||||
if (strcmp(timecode.c_str(), "00:00:01;00") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool timecodefunctions_time_to_timecode_test2()
|
||||
{
|
||||
Rational bizarre_timebase(156632219);
|
||||
|
||||
std::string timecode = Timecode::time_to_timecode(
|
||||
Rational(0), bizarre_timebase, Timecode::kTimecodeDropFrame);
|
||||
if (strcmp(timecode.c_str(), "INVALID TIMEBASE") != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Tester t;
|
||||
|
||||
t.add("Timecode::time_to_timecode",
|
||||
timecodefunctions_time_to_timecode_test);
|
||||
t.add("Timecode::time_to_timecode2",
|
||||
timecodefunctions_time_to_timecode_test2);
|
||||
|
||||
return t.exec();
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2023 Olive Studios LLC
|
||||
Modifications Copyright (C) 2025 mikesolar
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "util/timerange.h"
|
||||
#include "util/tests.h"
|
||||
|
||||
using namespace olive::core;
|
||||
|
||||
bool timerangelist_remove_test()
|
||||
{
|
||||
TimeRangeList t;
|
||||
|
||||
t.insert(TimeRange(0, 30));
|
||||
t.remove(TimeRange(2, 5));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool timerangelist_mergeadjacent_test()
|
||||
{
|
||||
TimeRangeList t;
|
||||
|
||||
// TimeRangeList should merge 1 and 3 together since they're adjacent
|
||||
t.insert(TimeRange(0, 6));
|
||||
t.insert(TimeRange(20, 30));
|
||||
t.insert(TimeRange(6, 10));
|
||||
|
||||
if (!(t.size() == 2)) {
|
||||
return false;
|
||||
}
|
||||
if (!(t.first() == TimeRange(20, 30))) {
|
||||
return false;
|
||||
}
|
||||
if (!(t.at(1) == TimeRange(0, 10))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TimeRangeList should ignore these because it's already contained
|
||||
TimeRangeList noop_test = t;
|
||||
|
||||
noop_test.insert(TimeRange(4, 7));
|
||||
if (!(noop_test == t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
noop_test.insert(TimeRange(0, 3));
|
||||
if (!(noop_test == t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
noop_test.insert(TimeRange(25, 30));
|
||||
if (!(noop_test == t)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TimeRangeList should combine all these together
|
||||
TimeRangeList combine_test_no_overlap = t;
|
||||
combine_test_no_overlap.insert(TimeRange(10, 20));
|
||||
if (!(combine_test_no_overlap.size() == 1)) {
|
||||
return false;
|
||||
}
|
||||
if (!(combine_test_no_overlap.first() == TimeRange(0, 30))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TimeRangeList combine_test_in_overlap = t;
|
||||
combine_test_in_overlap.insert(TimeRange(9, 20));
|
||||
if (!(combine_test_in_overlap.size() == 1)) {
|
||||
return false;
|
||||
}
|
||||
if (!(combine_test_in_overlap.first() == TimeRange(0, 30))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TimeRangeList combine_test_out_overlap = t;
|
||||
combine_test_out_overlap.insert(TimeRange(10, 21));
|
||||
if (!(combine_test_out_overlap.size() == 1)) {
|
||||
return false;
|
||||
}
|
||||
if (!(combine_test_out_overlap.first() == TimeRange(0, 30))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TimeRangeList combine_test_both_overlap = t;
|
||||
combine_test_both_overlap.insert(TimeRange(9, 21));
|
||||
if (!(combine_test_both_overlap.size() == 1)) {
|
||||
return false;
|
||||
}
|
||||
if (!(combine_test_both_overlap.first() == TimeRange(0, 30))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
Tester t;
|
||||
|
||||
t.add("TimeRangeList::remove", timerangelist_remove_test);
|
||||
t.add("TimeRangeList::merge_adjacent", timerangelist_mergeadjacent_test);
|
||||
|
||||
return t.exec();
|
||||
}
|
||||
Reference in New Issue
Block a user