• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 17920603410

22 Sep 2025 03:39PM UTC coverage: 71.97% (-0.05%) from 72.02%
17920603410

push

github

paulmthompson
all tests pass

277 of 288 new or added lines in 8 files covered. (96.18%)

520 existing lines in 35 files now uncovered.

40275 of 55961 relevant lines covered (71.97%)

1225.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.38
/src/DataManager/utils/TableView/computers/TimestampInIntervalComputer.test.cpp
1
#include <catch2/catch_test_macros.hpp>
2

3
#include "TimestampInIntervalComputer.h"
4
#include "utils/TableView/core/ExecutionPlan.h"
5
#include "utils/TableView/interfaces/IIntervalSource.h"
6
#include "TimeFrame/interval_data.hpp"
7
#include "TimeFrame/TimeFrame.hpp"
8

9
// Additional includes for extended testing
10
#include "DataManager.hpp"
11
#include "utils/TableView/ComputerRegistry.hpp"
12
#include "utils/TableView/adapters/DataManagerExtension.h"
13
#include "utils/TableView/core/TableView.h"
14
#include "utils/TableView/core/TableViewBuilder.h"
15
#include "utils/TableView/interfaces/IRowSelector.h"
16
#include "utils/TableView/pipeline/TablePipeline.hpp"
17
#include "utils/TableView/TableRegistry.hpp"
18
#include "DigitalTimeSeries/Digital_Interval_Series.hpp"
19

20
#include <memory>
21
#include <vector>
22
#include <cstdint>
23
#include <numeric>
24
#include <nlohmann/json.hpp>
25

26
/**
27
 * @brief Base test fixture for TimestampInIntervalComputer with realistic interval data
28
 * 
29
 * This fixture provides a DataManager populated with:
30
 * - TimeFrames with different granularities
31
 * - Interval data representing behavior periods  
32
 * - Stimulus events and digital intervals
33
 * - Cross-timeframe scenarios for testing timeframe conversion
34
 */
35
class TimestampInIntervalTestFixture {
36
protected:
37
    TimestampInIntervalTestFixture() {
7✔
38
        // Initialize the DataManager
39
        m_data_manager = std::make_unique<DataManager>();
7✔
40
        
41
        // Populate with test data
42
        populateWithTestData();
7✔
43
    }
7✔
44

45
    ~TimestampInIntervalTestFixture() = default;
7✔
46

47
    /**
48
     * @brief Get the DataManager instance
49
     */
50
    DataManager & getDataManager() { return *m_data_manager; }
14✔
51
    DataManager const & getDataManager() const { return *m_data_manager; }
52
    DataManager * getDataManagerPtr() { return m_data_manager.get(); }
53

54
private:
55
    std::unique_ptr<DataManager> m_data_manager;
56

57
    /**
58
     * @brief Populate the DataManager with test data
59
     */
60
    void populateWithTestData() {
7✔
61
        createTimeFrames();
7✔
62
        createIntervalData();
7✔
63
    }
7✔
64

65
    /**
66
     * @brief Create TimeFrame objects for different data streams
67
     */
68
    void createTimeFrames() {
7✔
69
        // Create "behavior_time" timeframe: 0 to 100 (101 points) - behavior tracking at 10Hz
70
        std::vector<int> behavior_time_values(101);
21✔
71
        std::iota(behavior_time_values.begin(), behavior_time_values.end(), 0);
7✔
72
        auto behavior_time_frame = std::make_shared<TimeFrame>(behavior_time_values);
7✔
73
        m_data_manager->setTime(TimeKey("behavior_time"), behavior_time_frame, true);
7✔
74

75
        // Create "stimulus_time" timeframe: 0, 5, 10, 15, ..., 100 (21 points) - stimulus at 2Hz
76
        std::vector<int> stimulus_time_values;
7✔
77
        stimulus_time_values.reserve(21);
7✔
78
        for (int i = 0; i <= 20; ++i) {
154✔
79
            stimulus_time_values.push_back(i * 5);
147✔
80
        }
81
        auto stimulus_time_frame = std::make_shared<TimeFrame>(stimulus_time_values);
7✔
82
        m_data_manager->setTime(TimeKey("stimulus_time"), stimulus_time_frame, true);
7✔
83

84
        // Create "high_res_time" timeframe: 0, 1, 2, ..., 200 (201 points) - high resolution at 20Hz
85
        std::vector<int> high_res_time_values(201);
21✔
86
        std::iota(high_res_time_values.begin(), high_res_time_values.end(), 0);
7✔
87
        auto high_res_time_frame = std::make_shared<TimeFrame>(high_res_time_values);
7✔
88
        m_data_manager->setTime(TimeKey("high_res_time"), high_res_time_frame, true);
7✔
89
    }
14✔
90

91
    /**
92
     * @brief Create interval data for testing
93
     */
94
    void createIntervalData() {
7✔
95
        // Create behavior periods on behavior_time
96
        auto behavior_intervals = std::make_shared<DigitalIntervalSeries>();
7✔
97
        
98
        // Exploration period 1: time 10-25
99
        behavior_intervals->addEvent(TimeFrameIndex(10), TimeFrameIndex(25));
7✔
100
        
101
        // Rest period: time 35-45  
102
        behavior_intervals->addEvent(TimeFrameIndex(35), TimeFrameIndex(45));
7✔
103
        
104
        // Exploration period 2: time 60-80
105
        behavior_intervals->addEvent(TimeFrameIndex(60), TimeFrameIndex(80));
7✔
106
        
107
        // Social interaction: time 85-95
108
        behavior_intervals->addEvent(TimeFrameIndex(85), TimeFrameIndex(95));
7✔
109

110
        m_data_manager->setData<DigitalIntervalSeries>("BehaviorPeriods", behavior_intervals, TimeKey("behavior_time"));
21✔
111

112
        // Create stimulus intervals on stimulus_time
113
        auto stimulus_intervals = std::make_shared<DigitalIntervalSeries>();
7✔
114
        
115
        // Stimulus 1: time 5-15 (index 1-3 = time 5-15)
116
        stimulus_intervals->addEvent(TimeFrameIndex(1), TimeFrameIndex(3));
7✔
117
        
118
        // Stimulus 2: time 25-35 (index 5-7 = time 25-35)
119
        stimulus_intervals->addEvent(TimeFrameIndex(5), TimeFrameIndex(7));
7✔
120
        
121
        // Stimulus 3: time 50-70 (index 10-14 = time 50-70)
122
        stimulus_intervals->addEvent(TimeFrameIndex(10), TimeFrameIndex(14));
7✔
123
        
124
        // Stimulus 4: time 90-100 (index 18-20 = time 90-100)
125
        stimulus_intervals->addEvent(TimeFrameIndex(18), TimeFrameIndex(20));
7✔
126

127
        m_data_manager->setData<DigitalIntervalSeries>("StimulusIntervals", stimulus_intervals, TimeKey("stimulus_time"));
21✔
128

129
        // Create simple test intervals on behavior_time for basic testing
130
        auto simple_intervals = std::make_shared<DigitalIntervalSeries>();
7✔
131
        simple_intervals->addEvent(TimeFrameIndex(5), TimeFrameIndex(15));   // time 5-15
7✔
132
        simple_intervals->addEvent(TimeFrameIndex(25), TimeFrameIndex(35));  // time 25-35
7✔
133
        simple_intervals->addEvent(TimeFrameIndex(50), TimeFrameIndex(60));  // time 50-60
7✔
134

135
        m_data_manager->setData<DigitalIntervalSeries>("SimpleIntervals", simple_intervals, TimeKey("behavior_time"));
21✔
136
    }
14✔
137
};
138

139
/**
140
 * @brief Test fixture combining TimestampInIntervalTestFixture with TableRegistry and TablePipeline
141
 * 
142
 * This fixture provides everything needed to test JSON-based table pipeline execution:
143
 * - DataManager with interval test data (from TimestampInIntervalTestFixture)
144
 * - TableRegistry for managing table configurations
145
 * - TablePipeline for executing JSON configurations
146
 */
147
class TimestampTableRegistryTestFixture : public TimestampInIntervalTestFixture {
148
protected:
149
    TimestampTableRegistryTestFixture()
5✔
150
        : TimestampInIntervalTestFixture() {
5✔
151
        // Use the DataManager's existing TableRegistry instead of creating a new one
152
        m_table_registry_ptr = getDataManager().getTableRegistry();
5✔
153

154
        // Initialize TablePipeline with the existing TableRegistry
155
        m_table_pipeline = std::make_unique<TablePipeline>(m_table_registry_ptr, &getDataManager());
5✔
156
    }
5✔
157

158
    ~TimestampTableRegistryTestFixture() = default;
5✔
159

160
    /**
161
     * @brief Get the TableRegistry instance
162
     * @return Reference to the TableRegistry
163
     */
164
    TableRegistry & getTableRegistry() { return *m_table_registry_ptr; }
5✔
165

166
    /**
167
     * @brief Get the TableRegistry instance (const version)
168
     * @return Const reference to the TableRegistry
169
     */
170
    TableRegistry const & getTableRegistry() const { return *m_table_registry_ptr; }
171

172
    /**
173
     * @brief Get a pointer to the TableRegistry
174
     * @return Raw pointer to the TableRegistry
175
     */
176
    TableRegistry * getTableRegistryPtr() { return m_table_registry_ptr; }
177

178
    /**
179
     * @brief Get the TablePipeline instance
180
     * @return Reference to the TablePipeline
181
     */
182
    TablePipeline & getTablePipeline() { return *m_table_pipeline; }
2✔
183

184
    /**
185
     * @brief Get the TablePipeline instance (const version)
186
     * @return Const reference to the TablePipeline
187
     */
188
    TablePipeline const & getTablePipeline() const { return *m_table_pipeline; }
189

190
    /**
191
     * @brief Get a pointer to the TablePipeline
192
     * @return Raw pointer to the TablePipeline
193
     */
194
    TablePipeline * getTablePipelinePtr() { return m_table_pipeline.get(); }
195

196
    /**
197
     * @brief Get the DataManagerExtension instance
198
     */
199
    std::shared_ptr<DataManagerExtension> getDataManagerExtension() { 
200
        if (!m_data_manager_extension) {
201
            m_data_manager_extension = std::make_shared<DataManagerExtension>(getDataManager());
202
        }
203
        return m_data_manager_extension; 
204
    }
205

206
private:
207
    TableRegistry * m_table_registry_ptr; // Points to DataManager's TableRegistry
208
    std::unique_ptr<TablePipeline> m_table_pipeline;
209
    std::shared_ptr<DataManagerExtension> m_data_manager_extension; // Lazy-initialized
210
};
211

212
// Mock implementation of IIntervalSource for testing
213
class MockTimestampIntervalSource : public IIntervalSource {
214
public:
215
    MockTimestampIntervalSource(std::string name, 
7✔
216
                               std::shared_ptr<TimeFrame> timeFrame,
217
                               std::vector<Interval> intervals)
218
        : m_name(std::move(name)), 
7✔
219
          m_timeFrame(std::move(timeFrame)), 
7✔
220
          m_intervals(std::move(intervals)) {}
14✔
221

222
    [[nodiscard]] auto getName() const -> std::string const & override {
1✔
223
        return m_name;
1✔
224
    }
225

226
    [[nodiscard]] auto getTimeFrame() const -> std::shared_ptr<TimeFrame> override {
×
227
        return m_timeFrame;
×
228
    }
229

230
    [[nodiscard]] auto size() const -> size_t override {
×
231
        return m_intervals.size();
×
232
    }
233

234
    auto getIntervals() -> std::vector<Interval> override {
×
235
        return m_intervals;
×
236
    }
237

238
    auto getIntervalsInRange(TimeFrameIndex start, TimeFrameIndex end, 
18✔
239
                            TimeFrame const * target_timeFrame) -> std::vector<Interval> override {
240
        std::vector<Interval> result;
18✔
241
        
242
        // Convert TimeFrameIndex to time values for comparison
243
        auto startTime = target_timeFrame->getTimeAtIndex(start);
18✔
244
        auto endTime = target_timeFrame->getTimeAtIndex(end);
18✔
245
        
246
        for (const auto& interval : m_intervals) {
48✔
247
            // Convert interval indices to time values using our timeframe
248
            auto intervalStartTime = m_timeFrame->getTimeAtIndex(TimeFrameIndex(interval.start));
30✔
249
            auto intervalEndTime = m_timeFrame->getTimeAtIndex(TimeFrameIndex(interval.end));
30✔
250
            
251
            // Check if intervals overlap in time
252
            if (intervalStartTime <= endTime && startTime <= intervalEndTime) {
30✔
253
                result.push_back(interval);
10✔
254
            }
255
        }
256
        
257
        return result;
18✔
258
    }
×
259

UNCOV
260
    auto getIntervalsWithIdsInRange(TimeFrameIndex start, TimeFrameIndex end, 
×
261
                                    TimeFrame const * target_timeFrame) -> std::vector<IntervalWithId> override {
UNCOV
262
        std::vector<IntervalWithId> result;
×
263
        
UNCOV
264
        return result;
×
265
    }
266

267
private:
268
    std::string m_name;
269
    std::shared_ptr<TimeFrame> m_timeFrame;
270
    std::vector<Interval> m_intervals;
271
};
272

273
TEST_CASE("DM - TV - TimestampInIntervalComputer Basic Functionality", "[TimestampInIntervalComputer]") {
4✔
274
    
275
    SECTION("Basic timestamp in interval detection") {
4✔
276
        // Create time frames
277
        std::vector<int> timeValues = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
3✔
278
        auto timeFrame = std::make_shared<TimeFrame>(timeValues);
1✔
279
        
280
        // Create intervals: [1,3] and [6,8]
281
        std::vector<Interval> intervals = {
1✔
282
            {1, 3},  // Interval 0: time 1-3
283
            {6, 8}   // Interval 1: time 6-8
284
        };
3✔
285
        
286
        auto intervalSource = std::make_shared<MockTimestampIntervalSource>(
1✔
287
            "TestIntervals", timeFrame, intervals);
1✔
288
        
289
        // Create timestamps to test: 0, 2, 5, 7, 9
290
        std::vector<TimeFrameIndex> timestamps = {
1✔
291
            TimeFrameIndex(0), TimeFrameIndex(2), TimeFrameIndex(5), 
292
            TimeFrameIndex(7), TimeFrameIndex(9)
293
        };
3✔
294
        
295
        ExecutionPlan plan(timestamps, timeFrame);
1✔
296
        
297
        // Create the computer
298
        TimestampInIntervalComputer computer(intervalSource, "TestIntervals");
3✔
299
        
300
        // Compute the results
301
        auto [results, entity_ids] = computer.compute(plan);
1✔
302
        
303
        // Verify results
304
        REQUIRE(results.size() == 5);
1✔
305
        REQUIRE(results[0] == false);  // Timestamp 0: not in any interval
1✔
306
        REQUIRE(results[1] == true);   // Timestamp 2: in interval [1,3]
1✔
307
        REQUIRE(results[2] == false);  // Timestamp 5: not in any interval
1✔
308
        REQUIRE(results[3] == true);   // Timestamp 7: in interval [6,8]
1✔
309
        REQUIRE(results[4] == false);  // Timestamp 9: not in any interval
1✔
310
    }
5✔
311
    
312
    SECTION("Edge cases - boundary conditions") {
4✔
313
        // Create time frames
314
        std::vector<int> timeValues = {0, 1, 2, 3, 4, 5};
3✔
315
        auto timeFrame = std::make_shared<TimeFrame>(timeValues);
1✔
316
        
317
        // Create interval: [2,4]
318
        std::vector<Interval> intervals = {{2, 4}};
3✔
319
        
320
        auto intervalSource = std::make_shared<MockTimestampIntervalSource>(
1✔
321
            "EdgeIntervals", timeFrame, intervals);
1✔
322
        
323
        // Test boundary timestamps: 1, 2, 3, 4, 5
324
        std::vector<TimeFrameIndex> timestamps = {
1✔
325
            TimeFrameIndex(1), TimeFrameIndex(2), TimeFrameIndex(3), 
326
            TimeFrameIndex(4), TimeFrameIndex(5)
327
        };
3✔
328
        
329
        ExecutionPlan plan(timestamps, timeFrame);
1✔
330
        TimestampInIntervalComputer computer(intervalSource, "EdgeIntervals");
3✔
331
        
332
        auto [results, entity_ids] = computer.compute(plan);
1✔
333
        
334
        REQUIRE(results.size() == 5);
1✔
335
        REQUIRE(results[0] == false);  // Timestamp 1: before interval
1✔
336
        REQUIRE(results[1] == true);   // Timestamp 2: at interval start
1✔
337
        REQUIRE(results[2] == true);   // Timestamp 3: inside interval
1✔
338
        REQUIRE(results[3] == true);   // Timestamp 4: at interval end
1✔
339
        REQUIRE(results[4] == false);  // Timestamp 5: after interval
1✔
340
    }
5✔
341
    
342
    SECTION("Empty intervals handling") {
4✔
343
        // Create time frames
344
        std::vector<int> timeValues = {0, 1, 2, 3, 4, 5};
3✔
345
        auto timeFrame = std::make_shared<TimeFrame>(timeValues);
1✔
346
        
347
        // Create empty intervals
348
        std::vector<Interval> intervals;
1✔
349
        auto intervalSource = std::make_shared<MockTimestampIntervalSource>(
1✔
350
            "EmptyIntervals", timeFrame, intervals);
1✔
351
        
352
        // Test some timestamps
353
        std::vector<TimeFrameIndex> timestamps = {
1✔
354
            TimeFrameIndex(0), TimeFrameIndex(2), TimeFrameIndex(4)
355
        };
3✔
356
        
357
        ExecutionPlan plan(timestamps, timeFrame);
1✔
358
        TimestampInIntervalComputer computer(intervalSource, "EmptyIntervals");
3✔
359
        
360
        auto [results, entity_ids] = computer.compute(plan);
1✔
361
        
362
        REQUIRE(results.size() == 3);
1✔
363
        REQUIRE(results[0] == false);  // No intervals to be inside
1✔
364
        REQUIRE(results[1] == false);  // No intervals to be inside
1✔
365
        REQUIRE(results[2] == false);  // No intervals to be inside
1✔
366
    }
5✔
367
    
368
    SECTION("Multiple overlapping intervals") {
4✔
369
        // Create time frames
370
        std::vector<int> timeValues = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
3✔
371
        auto timeFrame = std::make_shared<TimeFrame>(timeValues);
1✔
372
        
373
        // Create overlapping intervals: [1,4], [3,6], [5,8]
374
        std::vector<Interval> intervals = {
1✔
375
            {1, 4},  // Interval 0
376
            {3, 6},  // Interval 1 (overlaps with 0)
377
            {5, 8}   // Interval 2 (overlaps with 1)
378
        };
3✔
379
        
380
        auto intervalSource = std::make_shared<MockTimestampIntervalSource>(
1✔
381
            "OverlapIntervals", timeFrame, intervals);
1✔
382
        
383
        // Test timestamps throughout the range
384
        std::vector<TimeFrameIndex> timestamps = {
1✔
385
            TimeFrameIndex(0), TimeFrameIndex(2), TimeFrameIndex(4), 
386
            TimeFrameIndex(6), TimeFrameIndex(9)
387
        };
3✔
388
        
389
        ExecutionPlan plan(timestamps, timeFrame);
1✔
390
        TimestampInIntervalComputer computer(intervalSource, "OverlapIntervals");
3✔
391
        
392
        auto [results, entity_ids] = computer.compute(plan);
1✔
393
        
394
        REQUIRE(results.size() == 5);
1✔
395
        REQUIRE(results[0] == false);  // Timestamp 0: not in any interval
1✔
396
        REQUIRE(results[1] == true);   // Timestamp 2: in interval [1,4]
1✔
397
        REQUIRE(results[2] == true);   // Timestamp 4: in intervals [1,4] and [3,6]
1✔
398
        REQUIRE(results[3] == true);   // Timestamp 6: in intervals [3,6] and [5,8]
1✔
399
        REQUIRE(results[4] == false);  // Timestamp 9: not in any interval
1✔
400
    }
5✔
401
}
4✔
402

403
TEST_CASE("DM - TV - TimestampInIntervalComputer Error Handling", "[TimestampInIntervalComputer][Error]") {
2✔
404
    
405
    SECTION("Null interval source throws exception") {
2✔
406
        std::vector<int> timeValues = {0, 1, 2, 3, 4, 5};
3✔
407
        auto timeFrame = std::make_shared<TimeFrame>(timeValues);
1✔
408
        
409
        std::vector<TimeFrameIndex> timestamps = {TimeFrameIndex(0), TimeFrameIndex(1)};
3✔
410
        ExecutionPlan plan(timestamps, timeFrame);
1✔
411
        
412
        // Create computer with null source
413
        TimestampInIntervalComputer computer(nullptr, "NullSource");
3✔
414
        
415
        // Should throw an exception
416
        REQUIRE_THROWS_AS(computer.compute(plan), std::runtime_error);
2✔
417
    }
3✔
418
    
419
    SECTION("ExecutionPlan without TimeFrame throws exception") {
2✔
420
        std::vector<int> timeValues = {0, 1, 2, 3, 4, 5};
3✔
421
        auto timeFrame = std::make_shared<TimeFrame>(timeValues);
1✔
422
        
423
        std::vector<Interval> intervals = {{1, 3}};
3✔
424
        auto intervalSource = std::make_shared<MockTimestampIntervalSource>(
1✔
425
            "TestIntervals", timeFrame, intervals);
1✔
426
        
427
        // Create execution plan with null timeframe
428
        std::vector<TimeFrameIndex> timestamps = {TimeFrameIndex(0), TimeFrameIndex(1)};
3✔
429
        ExecutionPlan plan(timestamps, nullptr);
1✔
430
        
431
        TimestampInIntervalComputer computer(intervalSource, "TestIntervals");
3✔
432
        
433
        // Should throw an exception
434
        REQUIRE_THROWS_AS(computer.compute(plan), std::runtime_error);
2✔
435
    }
3✔
436
    
437
}
2✔
438

439
TEST_CASE("DM - TV - TimestampInIntervalComputer Dependency Tracking", "[TimestampInIntervalComputer][Dependencies]") {
2✔
440
    
441
    SECTION("getSourceDependency returns correct source name") {
2✔
442
        // Create minimal setup
443
        std::vector<int> timeValues = {0, 1, 2};
3✔
444
        auto timeFrame = std::make_shared<TimeFrame>(timeValues);
1✔
445
        
446
        std::vector<Interval> intervals = {{0, 1}};
3✔
447
        auto intervalSource = std::make_shared<MockTimestampIntervalSource>(
1✔
448
            "TestSource", timeFrame, intervals);
1✔
449
        
450
        // Create computer with custom source name
451
        TimestampInIntervalComputer computer(intervalSource, "CustomSourceName");
3✔
452
        
453
        // Test source dependency
454
        REQUIRE(computer.getSourceDependency() == "CustomSourceName");
1✔
455
    }
3✔
456
    
457
    SECTION("Default source name from interval source") {
2✔
458
        // Create minimal setup
459
        std::vector<int> timeValues = {0, 1, 2};
3✔
460
        auto timeFrame = std::make_shared<TimeFrame>(timeValues);
1✔
461
        
462
        std::vector<Interval> intervals = {{0, 1}};
3✔
463
        auto intervalSource = std::make_shared<MockTimestampIntervalSource>(
1✔
464
            "SourceName", timeFrame, intervals);
1✔
465
        
466
        // Create computer without custom source name (should use source->getName())
467
        TimestampInIntervalComputer computer(intervalSource);
3✔
468
        
469
        // Test source dependency
470
        REQUIRE(computer.getSourceDependency() == "SourceName");
1✔
471
    }
3✔
472
}
2✔
473

474
TEST_CASE_METHOD(TimestampInIntervalTestFixture, "DM - TV - TimestampInIntervalComputer with DataManager fixture", "[TimestampInIntervalComputer][DataManager][Fixture]") {
2✔
475
    
476
    SECTION("Test with behavior intervals from fixture") {
2✔
477
        auto& dm = getDataManager();
1✔
478
        auto dme = std::make_shared<DataManagerExtension>(dm);
1✔
479
        
480
        // Get the interval source from the DataManager
481
        auto behavior_source = dme->getIntervalSource("BehaviorPeriods");
3✔
482
        REQUIRE(behavior_source != nullptr);
1✔
483
        
484
        // Create timestamps to test around the behavior periods
485
        auto behavior_time_frame = dm.getTime(TimeKey("behavior_time"));
1✔
486
        std::vector<TimeFrameIndex> test_timestamps = {
1✔
487
            TimeFrameIndex(5),   // Before first period
488
            TimeFrameIndex(15),  // Inside first period (10-25)
489
            TimeFrameIndex(30),  // Between periods
490
            TimeFrameIndex(40),  // Inside second period (35-45)
491
            TimeFrameIndex(55),  // Between periods
492
            TimeFrameIndex(70),  // Inside third period (60-80)
493
            TimeFrameIndex(90),  // Inside fourth period (85-95)
494
            TimeFrameIndex(99)   // After all periods
495
        };
3✔
496
        
497
        auto row_selector = std::make_unique<TimestampSelector>(test_timestamps, behavior_time_frame);
1✔
498
        
499
        // Create TableView builder
500
        TableViewBuilder builder(dme);
1✔
501
        builder.setRowSelector(std::move(row_selector));
1✔
502
        
503
        // Add TimestampInIntervalComputer column
504
        builder.addColumn<bool>("InBehaviorPeriod", 
5✔
505
            std::make_unique<TimestampInIntervalComputer>(behavior_source, "BehaviorPeriods"));
2✔
506
        
507
        // Build the table
508
        TableView table = builder.build();
1✔
509
        
510
        // Verify table structure
511
        REQUIRE(table.getRowCount() == 8);
1✔
512
        REQUIRE(table.getColumnCount() == 1);
1✔
513
        REQUIRE(table.hasColumn("InBehaviorPeriod"));
3✔
514
        
515
        // Get the column data
516
        auto in_behavior = table.getColumnValues<bool>("InBehaviorPeriod");
3✔
517
        REQUIRE(in_behavior.size() == 8);
1✔
518
        
519
        // Verify the results based on our test data:
520
        // Periods: [10-25], [35-45], [60-80], [85-95]
521
        REQUIRE(in_behavior[0] == false);  // Timestamp 5: before all periods
1✔
522
        REQUIRE(in_behavior[1] == true);   // Timestamp 15: in period [10-25]
1✔
523
        REQUIRE(in_behavior[2] == false);  // Timestamp 30: between periods
1✔
524
        REQUIRE(in_behavior[3] == true);   // Timestamp 40: in period [35-45]
1✔
525
        REQUIRE(in_behavior[4] == false);  // Timestamp 55: between periods
1✔
526
        REQUIRE(in_behavior[5] == true);   // Timestamp 70: in period [60-80]
1✔
527
        REQUIRE(in_behavior[6] == true);   // Timestamp 90: in period [85-95]
1✔
528
        REQUIRE(in_behavior[7] == false);  // Timestamp 99: after all periods
1✔
529
    }
3✔
530
    
531
    SECTION("Test cross-timeframe interval detection") {
2✔
532
        auto& dm = getDataManager();
1✔
533
        auto dme = std::make_shared<DataManagerExtension>(dm);
1✔
534
        
535
        // Get sources from different timeframes
536
        auto behavior_source = dme->getIntervalSource("BehaviorPeriods");  // behavior_time frame
3✔
537
        auto stimulus_source = dme->getIntervalSource("StimulusIntervals");   // stimulus_time frame
3✔
538
        
539
        REQUIRE(behavior_source != nullptr);
1✔
540
        REQUIRE(stimulus_source != nullptr);
1✔
541
        
542
        // Verify they have different timeframes
543
        auto behavior_tf = behavior_source->getTimeFrame();
1✔
544
        auto stimulus_tf = stimulus_source->getTimeFrame();
1✔
545
        REQUIRE(behavior_tf != stimulus_tf);
1✔
546
        REQUIRE(behavior_tf->getTotalFrameCount() == 101);  // behavior_time: 0-100
1✔
547
        REQUIRE(stimulus_tf->getTotalFrameCount() == 21);   // stimulus_time: 0,5,10,...,100
1✔
548
        
549
        // Create timestamps on high resolution timeframe
550
        auto high_res_tf = dm.getTime(TimeKey("high_res_time"));
1✔
551
        std::vector<TimeFrameIndex> test_timestamps = {
1✔
552
            TimeFrameIndex(10),   // time 10 (should be in stimulus interval [5-15])
553
            TimeFrameIndex(20),   // time 20 (should not be in any stimulus interval)
554
            TimeFrameIndex(60),   // time 60 (should be in stimulus interval [50-70])
555
            TimeFrameIndex(95)    // time 95 (should be in stimulus interval [90-100])
556
        };
3✔
557
        
558
        auto row_selector = std::make_unique<TimestampSelector>(test_timestamps, high_res_tf);
1✔
559
        
560
        TableViewBuilder builder(dme);
1✔
561
        builder.setRowSelector(std::move(row_selector));
1✔
562
        
563
        // Add cross-timeframe analysis column
564
        builder.addColumn<bool>("InStimulusInterval", 
5✔
565
            std::make_unique<TimestampInIntervalComputer>(stimulus_source, "StimulusIntervals"));
2✔
566
        
567
        // Build and verify the table
568
        TableView table = builder.build();
1✔
569
        
570
        REQUIRE(table.getRowCount() == 4);
1✔
571
        REQUIRE(table.getColumnCount() == 1);
1✔
572
        
573
        auto in_stimulus = table.getColumnValues<bool>("InStimulusInterval");
3✔
574
        REQUIRE(in_stimulus.size() == 4);
1✔
575
        
576
        // Verify results (exact values depend on timeframe conversion)
577
        // All should be valid boolean results
578
        for (size_t i = 0; i < 4; ++i) {
5✔
579
            // Just verify they are valid boolean values (no exceptions during computation)
580
            REQUIRE((in_stimulus[i] == true || in_stimulus[i] == false));
4✔
581
        }
582
        
583
        std::cout << "Cross-timeframe test results - timestamps at times 10, 20, 60, 95:" << std::endl;
1✔
584
        for (size_t i = 0; i < 4; ++i) {
5✔
585
            std::cout << "  Timestamp " << test_timestamps[i].getValue() << ": " 
4✔
586
                      << (in_stimulus[i] ? "inside" : "outside") << " stimulus interval" << std::endl;
4✔
587
        }
588
    }
3✔
589
}
2✔
590

591
TEST_CASE_METHOD(TimestampTableRegistryTestFixture, "DM - TV - TimestampInIntervalComputer via ComputerRegistry", "[TimestampInIntervalComputer][Registry]") {
3✔
592
    
593
    SECTION("Verify TimestampInIntervalComputer is registered in ComputerRegistry") {
3✔
594
        auto& registry = getTableRegistry().getComputerRegistry();
1✔
595
        
596
        // Check that the computer is registered
597
        auto computer_info = registry.findComputerInfo("Timestamp In Interval");
3✔
598
        REQUIRE(computer_info != nullptr);
1✔
599
        
600
        // Verify computer info details
601
        REQUIRE(computer_info->name == "Timestamp In Interval");
1✔
602
        REQUIRE(computer_info->outputType == typeid(bool));
1✔
603
        REQUIRE(computer_info->outputTypeName == "bool");
1✔
604
        REQUIRE(computer_info->requiredRowSelector == RowSelectorType::Timestamp);
1✔
605
        REQUIRE(computer_info->requiredSourceType == typeid(std::shared_ptr<IIntervalSource>));
1✔
606
        REQUIRE(computer_info->isVectorType == false);
1✔
607
        REQUIRE(computer_info->elementType == typeid(bool));
1✔
608
    }
3✔
609
    
610
    SECTION("Create TimestampInIntervalComputer via ComputerRegistry") {
3✔
611
        auto& dm = getDataManager();
1✔
612
        auto dme = std::make_shared<DataManagerExtension>(dm);
1✔
613
        auto& registry = getTableRegistry().getComputerRegistry();
1✔
614
        
615
        // Get interval source for testing
616
        auto simple_source = dme->getIntervalSource("SimpleIntervals");
3✔
617
        REQUIRE(simple_source != nullptr);
1✔
618
        
619
        // Create computer via registry
620
        std::map<std::string, std::string> empty_params;
1✔
621
        
622
        auto computer = registry.createTypedComputer<bool>(
1✔
623
            "Timestamp In Interval", simple_source, empty_params);
3✔
624
        
625
        REQUIRE(computer != nullptr);
1✔
626
        
627
        // Test that the created computer works correctly
628
        auto behavior_time_frame = dm.getTime(TimeKey("behavior_time"));
1✔
629
        
630
        // Create test timestamps
631
        std::vector<TimeFrameIndex> test_timestamps = {
1✔
632
            TimeFrameIndex(3),   // Before first interval
633
            TimeFrameIndex(10),  // Inside first interval [5-15]
634
            TimeFrameIndex(20),  // Between intervals
635
            TimeFrameIndex(30),  // Inside second interval [25-35]
636
            TimeFrameIndex(55)   // Inside third interval [50-60]
637
        };
3✔
638
        
639
        auto row_selector = std::make_unique<TimestampSelector>(test_timestamps, behavior_time_frame);
1✔
640
        
641
        TableViewBuilder builder(dme);
1✔
642
        builder.setRowSelector(std::move(row_selector));
1✔
643
        
644
        // Use the registry-created computer
645
        builder.addColumn("RegistryInInterval", std::move(computer));
3✔
646
        
647
        // Build and verify the table
648
        TableView table = builder.build();
1✔
649
        
650
        REQUIRE(table.getRowCount() == 5);
1✔
651
        REQUIRE(table.getColumnCount() == 1);
1✔
652
        REQUIRE(table.hasColumn("RegistryInInterval"));
3✔
653
        
654
        auto results = table.getColumnValues<bool>("RegistryInInterval");
3✔
655
        REQUIRE(results.size() == 5);
1✔
656
        
657
        // Verify expected results based on SimpleIntervals: [5-15], [25-35], [50-60]
658
        REQUIRE(results[0] == false);  // Timestamp 3: before first interval
1✔
659
        REQUIRE(results[1] == true);   // Timestamp 10: inside first interval
1✔
660
        REQUIRE(results[2] == false);  // Timestamp 20: between intervals
1✔
661
        REQUIRE(results[3] == true);   // Timestamp 30: inside second interval
1✔
662
        REQUIRE(results[4] == true);   // Timestamp 55: inside third interval
1✔
663
        
664
        std::cout << "Registry test results:" << std::endl;
1✔
665
        for (size_t i = 0; i < 5; ++i) {
6✔
666
            std::cout << "  Timestamp " << test_timestamps[i].getValue() << ": " 
5✔
667
                      << (results[i] ? "inside" : "outside") << " interval" << std::endl;
5✔
668
        }
669
    }
4✔
670
    
671
    SECTION("Compare registry-created vs direct-created computers") {
3✔
672
        auto& dm = getDataManager();
1✔
673
        auto dme = std::make_shared<DataManagerExtension>(dm);
1✔
674
        auto& registry = getTableRegistry().getComputerRegistry();
1✔
675
        
676
        auto simple_source = dme->getIntervalSource("SimpleIntervals");
3✔
677
        REQUIRE(simple_source != nullptr);
1✔
678
        
679
        // Create computer via registry
680
        std::map<std::string, std::string> empty_params;
1✔
681
        auto registry_computer = registry.createTypedComputer<bool>(
1✔
682
            "Timestamp In Interval", simple_source, empty_params);
3✔
683
        
684
        // Create computer directly
685
        auto direct_computer = std::make_unique<TimestampInIntervalComputer>(
1✔
686
            simple_source, "SimpleIntervals");
1✔
687
        
688
        REQUIRE(registry_computer != nullptr);
1✔
689
        REQUIRE(direct_computer != nullptr);
1✔
690
        
691
        // Test both computers with the same data
692
        auto behavior_time_frame = dm.getTime(TimeKey("behavior_time"));
1✔
693
        std::vector<TimeFrameIndex> test_timestamps = {
1✔
694
            TimeFrameIndex(10), TimeFrameIndex(30), TimeFrameIndex(55)
695
        };
3✔
696
        
697
        ExecutionPlan plan(test_timestamps, behavior_time_frame);
1✔
698
        
699
        auto [registry_result, registryEntity_ids] = registry_computer->compute(plan);
1✔
700
        auto [direct_result, directEntity_ids] = direct_computer->compute(plan);
1✔
701
        
702
        REQUIRE(registry_result.size() == 3);
1✔
703
        REQUIRE(direct_result.size() == 3);
1✔
704
        
705
        // Results should be identical
706
        for (size_t i = 0; i < 3; ++i) {
4✔
707
            REQUIRE(registry_result[i] == direct_result[i]);
3✔
708
        }
709
        
710
        std::cout << "Comparison test - results are identical:" << std::endl;
1✔
711
        for (size_t i = 0; i < 3; ++i) {
4✔
712
            std::cout << "  Timestamp " << test_timestamps[i].getValue() 
3✔
713
                      << ": Registry=" << (registry_result[i] ? "true" : "false")
6✔
714
                      << ", Direct=" << (direct_result[i] ? "true" : "false") << std::endl;
6✔
715
        }
716
    }
4✔
717
}
3✔
718

719
TEST_CASE_METHOD(TimestampTableRegistryTestFixture, "DM - TV - TimestampInIntervalComputer via JSON TablePipeline", "[TimestampInIntervalComputer][JSON][Pipeline]") {
2✔
720
    
721
    SECTION("Test TimestampInIntervalComputer via JSON pipeline") {
2✔
722
        // JSON configuration for testing TimestampInIntervalComputer through TablePipeline
723
        char const * json_config = R"({
1✔
724
            "metadata": {
725
                "name": "Timestamp In Interval Test",
726
                "description": "Test JSON execution of TimestampInIntervalComputer",
727
                "version": "1.0"
728
            },
729
            "tables": [
730
                {
731
                    "table_id": "timestamp_in_interval_test",
732
                    "name": "Timestamp In Interval Test Table",
733
                    "description": "Test table using TimestampInIntervalComputer",
734
                    "row_selector": {
735
                        "type": "timestamp",
736
                        "source": "behavior_time"
737
                    },
738
                    "columns": [
739
                        {
740
                            "name": "InBehaviorPeriod",
741
                            "description": "True if timestamp is within any behavior period",
742
                            "data_source": "BehaviorPeriods",
743
                            "computer": "Timestamp In Interval"
744
                        },
745
                        {
746
                            "name": "InSimpleInterval",
747
                            "description": "True if timestamp is within any simple interval",
748
                            "data_source": "SimpleIntervals",
749
                            "computer": "Timestamp In Interval"
750
                        }
751
                    ]
752
                }
753
            ]
754
        })";
755

756
        auto& pipeline = getTablePipeline();
1✔
757

758
        // Parse the JSON configuration
759
        nlohmann::json json_obj = nlohmann::json::parse(json_config);
1✔
760

761
        // Load configuration into pipeline
762
        bool load_success = pipeline.loadFromJson(json_obj);
1✔
763
        REQUIRE(load_success);
1✔
764

765
        // Verify configuration was loaded correctly
766
        auto table_configs = pipeline.getTableConfigurations();
1✔
767
        REQUIRE(table_configs.size() == 1);
1✔
768

769
        auto const& config = table_configs[0];
1✔
770
        REQUIRE(config.table_id == "timestamp_in_interval_test");
1✔
771
        REQUIRE(config.name == "Timestamp In Interval Test Table");
1✔
772
        REQUIRE(config.columns.size() == 2);
1✔
773

774
        // Verify column configurations
775
        auto const& column1 = config.columns[0];
1✔
776
        REQUIRE(column1["name"] == "InBehaviorPeriod");
1✔
777
        REQUIRE(column1["computer"] == "Timestamp In Interval");
1✔
778
        REQUIRE(column1["data_source"] == "BehaviorPeriods");
1✔
779

780
        auto const& column2 = config.columns[1];
1✔
781
        REQUIRE(column2["name"] == "InSimpleInterval");
1✔
782
        REQUIRE(column2["computer"] == "Timestamp In Interval");
1✔
783
        REQUIRE(column2["data_source"] == "SimpleIntervals");
1✔
784

785
        // Verify row selector configuration
786
        REQUIRE(config.row_selector["type"] == "timestamp");
1✔
787
        REQUIRE(config.row_selector["source"] == "behavior_time");
1✔
788

789
        std::cout << "JSON pipeline configuration loaded and parsed successfully" << std::endl;
1✔
790

791
        // Execute the pipeline
792
        auto pipeline_result = pipeline.execute([](int table_index, std::string const& table_name, int table_progress, int overall_progress) {
2✔
793
            std::cout << "Building table " << table_index << " (" << table_name << "): "
4✔
794
                      << table_progress << "% (Overall: " << overall_progress << "%)" << std::endl;
4✔
795
        });
2✔
796

797
        if (pipeline_result.success) {
1✔
798
            std::cout << "Pipeline executed successfully!" << std::endl;
1✔
799
            std::cout << "Tables completed: " << pipeline_result.tables_completed << "/" << pipeline_result.total_tables << std::endl;
1✔
800
            std::cout << "Execution time: " << pipeline_result.total_execution_time_ms << " ms" << std::endl;
1✔
801

802
            // Verify the built table exists
803
            auto& registry = getTableRegistry();
1✔
804
            REQUIRE(registry.hasTable("timestamp_in_interval_test"));
3✔
805

806
            // Get the built table and verify its structure
807
            auto built_table = registry.getBuiltTable("timestamp_in_interval_test");
3✔
808
            REQUIRE(built_table != nullptr);
1✔
809

810
            // Check that the table has the expected columns
811
            auto column_names = built_table->getColumnNames();
1✔
812
            std::cout << "Built table has " << column_names.size() << " columns" << std::endl;
1✔
813
            for (auto const& name : column_names) {
3✔
814
                std::cout << "  Column: " << name << std::endl;
2✔
815
            }
816

817
            REQUIRE(column_names.size() == 2);
1✔
818
            REQUIRE(built_table->hasColumn("InBehaviorPeriod"));
3✔
819
            REQUIRE(built_table->hasColumn("InSimpleInterval"));
3✔
820

821
            // Verify table has 101 rows (one for each timestamp in behavior_time: 0-100)
822
            REQUIRE(built_table->getRowCount() == 101);
1✔
823

824
            // Get and verify the computed values
825
            auto behavior_results = built_table->getColumnValues<bool>("InBehaviorPeriod");
3✔
826
            auto simple_results = built_table->getColumnValues<bool>("InSimpleInterval");
3✔
827

828
            REQUIRE(behavior_results.size() == 101);
1✔
829
            REQUIRE(simple_results.size() == 101);
1✔
830

831
            // Verify some expected results based on our test data:
832
            // BehaviorPeriods: [10-25], [35-45], [60-80], [85-95]
833
            // SimpleIntervals: [5-15], [25-35], [50-60]
834
            
835
            // Check specific timestamps
836
            REQUIRE(behavior_results[5] == false);   // Timestamp 5: not in behavior periods
1✔
837
            REQUIRE(simple_results[5] == true);      // Timestamp 5: in simple interval [5-15]
1✔
838
            
839
            REQUIRE(behavior_results[15] == true);   // Timestamp 15: in behavior period [10-25] 
1✔
840
            REQUIRE(simple_results[15] == true);     // Timestamp 15: in simple interval [5-15]
1✔
841
            
842
            REQUIRE(behavior_results[30] == false);  // Timestamp 30: not in behavior periods
1✔
843
            REQUIRE(simple_results[30] == true);     // Timestamp 30: in simple interval [25-35]
1✔
844
            
845
            REQUIRE(behavior_results[40] == true);   // Timestamp 40: in behavior period [35-45]
1✔
846
            REQUIRE(simple_results[40] == false);    // Timestamp 40: not in simple intervals
1✔
847
            
848
            REQUIRE(behavior_results[70] == true);   // Timestamp 70: in behavior period [60-80]
1✔
849
            REQUIRE(simple_results[70] == false);    // Timestamp 70: not in simple intervals
1✔
850
            
851
            REQUIRE(behavior_results[90] == true);   // Timestamp 90: in behavior period [85-95]
1✔
852
            REQUIRE(simple_results[90] == false);    // Timestamp 90: not in simple intervals
1✔
853
            
854
            // All values should be valid booleans
855
            for (size_t i = 0; i < 101; ++i) {
102✔
856
                REQUIRE((behavior_results[i] == true || behavior_results[i] == false));
101✔
857
                REQUIRE((simple_results[i] == true || simple_results[i] == false));
101✔
858
            }
859
            
860
            // Print some sample results for verification
861
            std::vector<int> sample_timestamps = {5, 10, 15, 20, 30, 40, 55, 70, 90};
3✔
862
            std::cout << "Sample results from full timeframe:" << std::endl;
1✔
863
            for (int ts : sample_timestamps) {
10✔
864
                std::cout << "Timestamp " << ts 
9✔
865
                          << ": InBehaviorPeriod=" << (behavior_results[ts] ? "true" : "false")
18✔
866
                          << ", InSimpleInterval=" << (simple_results[ts] ? "true" : "false") << std::endl;
18✔
867
            }
868

869
        } else {
1✔
UNCOV
870
            std::cout << "Pipeline execution failed: " << pipeline_result.error_message << std::endl;
×
UNCOV
871
            FAIL("Pipeline execution failed: " + pipeline_result.error_message);
×
872
        }
873
    }
3✔
874
    
875
    SECTION("Test with cross-timeframe intervals via JSON") {
2✔
876
        char const * json_config = R"({
1✔
877
            "metadata": {
878
                "name": "Cross-Timeframe Timestamp Test",
879
                "description": "Test cross-timeframe timestamp interval detection"
880
            },
881
            "tables": [
882
                {
883
                    "table_id": "cross_timeframe_test",
884
                    "name": "Cross-Timeframe Test Table",
885
                    "description": "Test table with cross-timeframe interval detection",
886
                    "row_selector": {
887
                        "type": "timestamp",
888
                        "source": "high_res_time"
889
                    },
890
                    "columns": [
891
                        {
892
                            "name": "InStimulusInterval",
893
                            "description": "True if timestamp overlaps with stimulus intervals",
894
                            "data_source": "StimulusIntervals",
895
                            "computer": "Timestamp In Interval"
896
                        }
897
                    ]
898
                }
899
            ]
900
        })";
901

902
        auto& pipeline = getTablePipeline();
1✔
903
        nlohmann::json json_obj = nlohmann::json::parse(json_config);
1✔
904
        
905
        bool load_success = pipeline.loadFromJson(json_obj);
1✔
906
        REQUIRE(load_success);
1✔
907

908
        auto table_configs = pipeline.getTableConfigurations();
1✔
909
        REQUIRE(table_configs.size() == 1);
1✔
910
        
911
        auto const& config = table_configs[0];
1✔
912
        REQUIRE(config.row_selector["source"] == "high_res_time");
1✔
913
        REQUIRE(config.columns[0]["data_source"] == "StimulusIntervals");
1✔
914

915
        std::cout << "Cross-timeframe JSON configuration parsed successfully" << std::endl;
1✔
916
        
917
        // Execute the pipeline
918
        auto pipeline_result = pipeline.execute();
1✔
919
        
920
        if (pipeline_result.success) {
1✔
921
            std::cout << "✓ Cross-timeframe pipeline executed successfully!" << std::endl;
1✔
922
            
923
            auto& registry = getTableRegistry();
1✔
924
            auto built_table = registry.getBuiltTable("cross_timeframe_test");
3✔
925
            REQUIRE(built_table != nullptr);
1✔
926
            
927
            REQUIRE(built_table->getRowCount() == 201);  // high_res_time has 201 points (0-200)
1✔
928
            REQUIRE(built_table->getColumnCount() == 1);
1✔
929
            REQUIRE(built_table->hasColumn("InStimulusInterval"));
3✔
930
            
931
            auto results = built_table->getColumnValues<bool>("InStimulusInterval");
3✔
932
            REQUIRE(results.size() == 201);
1✔
933
            
934
            // Verify all results are valid
935
            for (size_t i = 0; i < 201; ++i) {
202✔
936
                REQUIRE((results[i] == true || results[i] == false));
201✔
937
            }
938
            
939
            // Print some sample results for verification
940
            std::vector<int> sample_timestamps = {10, 30, 60, 95};
3✔
941
            std::cout << "Sample cross-timeframe results:" << std::endl;
1✔
942
            for (int ts : sample_timestamps) {
5✔
943
                if (ts < 201) {  // Make sure index is valid
4✔
944
                    std::cout << "High-res timestamp " << ts 
4✔
945
                              << ": " << (results[ts] ? "inside" : "outside") << " stimulus interval" << std::endl;
4✔
946
                }
947
            }
948
            
949
        } else {
1✔
UNCOV
950
            FAIL("Cross-timeframe pipeline execution failed: " + pipeline_result.error_message);
×
951
        }
952
    }
3✔
953
}
2✔
954

955
// Keep the original basic tests for backward compatibility
956
TEST_CASE("TimestampInIntervalComputer basic integration", "[TimestampInIntervalComputer]") {
1✔
957
    DataManager dm;
1✔
958

959
    // Build a simple timeframe 0..9
960
    std::vector<int> times(10); for (int i=0;i<10;++i) times[i]=i;
13✔
961
    auto tf = std::make_shared<TimeFrame>(times);
1✔
962
    dm.setTime(TimeKey("cam"), tf, true);
1✔
963

964
    // Create digital interval series: [2,4] and [7,8]
965
    auto dis = std::make_shared<DigitalIntervalSeries>();
1✔
966
    dis->addEvent(Interval{2,4});
1✔
967
    dis->addEvent(Interval{7,8});
1✔
968
    dm.setData<DigitalIntervalSeries>("Intervals", dis, TimeKey("cam"));
3✔
969

970
    auto dme = std::make_shared<DataManagerExtension>(dm);
1✔
971

972
    // Timestamps: 1,3,5,8
973
    std::vector<TimeFrameIndex> ts = {TimeFrameIndex(1), TimeFrameIndex(3), TimeFrameIndex(5), TimeFrameIndex(8)};
3✔
974
    auto selector = std::make_unique<TimestampSelector>(ts, tf);
1✔
975

976
    // Build computer directly
977
    auto src = dme->getIntervalSource("Intervals");
3✔
978
    REQUIRE(src != nullptr);
1✔
979
    auto comp = std::make_unique<TimestampInIntervalComputer>(src, src->getName());
1✔
980

981
    TableViewBuilder builder(dme);
1✔
982
    builder.setRowSelector(std::move(selector));
1✔
983
    builder.addColumn<bool>("Inside", std::move(comp));
3✔
984
    auto table = builder.build();
1✔
985

986
    auto const & vals = table.getColumnValues<bool>("Inside");
3✔
987
    REQUIRE(vals.size() == 4);
1✔
988
    // 1:false, 3:true, 5:false, 8:true
989
    REQUIRE(vals[0] == false);
1✔
990
    REQUIRE(vals[1] == true);
1✔
991
    REQUIRE(vals[2] == false);
1✔
992
    REQUIRE(vals[3] == true);
1✔
993
}
2✔
994

995
TEST_CASE("TimestampInIntervalComputer via registry", "[TimestampInIntervalComputer][Registry]") {
1✔
996
    DataManager dm;
1✔
997
    std::vector<int> times(6); for (int i=0;i<6;++i) times[i]=i;
9✔
998
    auto tf = std::make_shared<TimeFrame>(times);
1✔
999
    dm.setTime(TimeKey("cam"), tf, true);
1✔
1000

1001
    auto dis = std::make_shared<DigitalIntervalSeries>();
1✔
1002
    dis->addEvent(Interval{0,2});
1✔
1003
    dis->addEvent(Interval{4,5});
1✔
1004
    dm.setData<DigitalIntervalSeries>("DInt", dis, TimeKey("cam"));
3✔
1005

1006
    auto dme = std::make_shared<DataManagerExtension>(dm);
1✔
1007
    auto src = dme->getIntervalSource("DInt");
3✔
1008
    REQUIRE(src != nullptr);
1✔
1009

1010
    ComputerRegistry registry;
1✔
1011
    DataSourceVariant variant = DataSourceVariant{src};
1✔
1012

1013
    auto typed = registry.createTypedComputer<bool>("Timestamp In Interval", variant, {});
3✔
1014
    REQUIRE(typed != nullptr);
1✔
1015

1016
    // Build table over timestamps 0..5
1017
    std::vector<TimeFrameIndex> ts = {TimeFrameIndex(0), TimeFrameIndex(1), TimeFrameIndex(2), TimeFrameIndex(3), TimeFrameIndex(4), TimeFrameIndex(5)};
3✔
1018
    auto selector = std::make_unique<TimestampSelector>(ts, tf);
1✔
1019

1020
    TableViewBuilder builder(dme);
1✔
1021
    builder.setRowSelector(std::move(selector));
1✔
1022
    builder.addColumn<bool>("InInt", std::move(typed));
3✔
1023
    auto table = builder.build();
1✔
1024

1025
    auto const & vals = table.getColumnValues<bool>("InInt");
3✔
1026
    REQUIRE(vals.size() == 6);
1✔
1027
    // [0,2] => true,true,true,false,true,true
1028
    REQUIRE(vals[0] == true);
1✔
1029
    REQUIRE(vals[1] == true);
1✔
1030
    REQUIRE(vals[2] == true);
1✔
1031
    REQUIRE(vals[3] == false);
1✔
1032
    REQUIRE(vals[4] == true);
1✔
1033
    REQUIRE(vals[5] == true);
1✔
1034
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc