Project
Loading...
Searching...
No Matches
testWorkflowSession.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#define BOOST_TEST_MODULE ITSMFT workflow session
13#define BOOST_TEST_MAIN
14#define BOOST_TEST_DYN_LINK
15#include <boost/test/unit_test.hpp>
17#include <boost/mpl/list.hpp>
18#include <type_traits>
19#include <any>
20#include <algorithm>
21#include <array>
22#include <vector>
23#include <map>
27
28using namespace o2::itsmft;
29using namespace o2::itsmft::tracking;
30using LayerCounts = boost::mpl::list<std::integral_constant<int, ITSNLayers>, std::integral_constant<int, MFTNLayers>>;
31namespace
32{
33struct FieldFixture {
34 FieldFixture() { o2::base::Propagator::initFieldFromGRP(0.f, 0.f, true, false); }
35};
36BOOST_GLOBAL_FIXTURE(FieldFixture);
37
38template <int N>
39struct Rig {
40 static constexpr auto Detector = N == ITSNLayers ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT;
41 WorkflowSession session{N == ITSNLayers ? "ITS" : "MFT", N};
42 Tracker tracker;
43 TrackerTraits traits;
44 std::shared_ptr<tbb::task_arena> arena;
45 TopologyDictionary dictionary;
46 std::array<LayerId, N> mapping{};
47 std::vector<ROFRecord> rofs{{{100, 5}, 0, 0, 0}};
48 std::vector<CompClusterExt> clusters;
49
50 explicit Rig(bool drop = false, size_t memory = std::numeric_limits<size_t>::max())
51 {
52 auto parameters = test::makeTestTrackingParameters(Detector);
53 parameters.UseDiamond = true;
54 auto plan = test::makeTrackingPlan(parameters);
55 plan.execution = {memory, drop};
58 TrackerInitialization init{catalog, {0}, {}, std::move(plan), std::make_shared<BoundedMemoryResource>()};
59 BOOST_REQUIRE(tracker.initialize(session.frame, init));
60 traits.setNThreads(1, arena);
61 for (int layer = 0; layer < N; ++layer) {
62 mapping[layer] = LayerId{static_cast<uint16_t>(layer)};
63 }
64 configure();
65 }
66 void configure()
67 {
68 std::vector<o2::its::LayerTiming> timings(N);
69 std::fill(timings.begin(), timings.end(), o2::its::LayerTiming{.mNROFsTF = 1, .mROFLength = 40});
70 session.configureTiming(timings, [](int) { return true; });
71 }
73 {
75 input.detector = Detector;
76 input.id = ClusterSourceId{0};
77 input.rofs = rofs;
78 input.clusters = clusters;
79 input.dictionary = &dictionary;
80 input.layerToSurface = mapping;
81 return input;
82 }
83 void checkClean()
84 {
85 BOOST_CHECK_EQUAL(session.frame.getTotalMeasurements(), 0u);
86 BOOST_CHECK(session.externalIndices.empty());
87 BOOST_CHECK(session.clusterSizes.empty());
88 BOOST_CHECK_EQUAL(session.frame.getROFViews().overlap.mLayerCount, 0);
89 }
90};
91} // namespace
92
93BOOST_AUTO_TEST_CASE_TEMPLATE(ValidEmptyInputCompletesBeforeCleanup, Count, LayerCounts)
94{
95 {
96 Rig<Count::value> rig;
97 int loaded = 0, completed = 0;
98 {
99 auto cleanup = rig.session.cleanupOnExit();
100 const auto outcome = rig.session.process(rig.tracker, rig.traits, rig.source(), [&](const o2::InteractionRecord& origin) {
101 ++loaded;
102 BOOST_CHECK(origin == rig.rofs.front().getBCData());
103 BOOST_CHECK_EQUAL(rig.session.frame.getTotalMeasurements(), 0u);
104 BOOST_CHECK_EQUAL(rig.session.frame.getROFViews().overlap.mLayerCount, Count::value); }, [&](const TrackingStatistics& result) {
105 ++completed;
106 BOOST_CHECK(result.elapsedMs > 0.f);
107 BOOST_REQUIRE_EQUAL(result.acceptedTrackCounts.size(), 1u);
108 BOOST_CHECK_EQUAL(result.acceptedTrackCounts[0], 0u); });
109 BOOST_CHECK(decideCATrackerPublicationAction(true, outcome) == CATrackerPublicationAction::PublishActiveResult);
110 }
111 BOOST_CHECK_EQUAL(loaded, 1);
112 BOOST_CHECK_EQUAL(completed, 1);
113 rig.checkClean();
114 }
115}
116
117BOOST_AUTO_TEST_CASE_TEMPLATE(ROFViewsAreBoundIndependentlyOfClusterLoading, Count, LayerCounts)
118{
119 Rig<Count::value> rig;
120 const auto views = rig.session.frame.getROFViews();
121 const auto source = rig.source();
122 auto& frame = rig.session.frame;
123 BOOST_REQUIRE_NO_THROW(loadTimeFrameSources(frame, gsl::span<const ClusterSourceInput>{&source, 1},
124 frame.getDetectorConfiguration().getSurfaceCatalog()));
125 BOOST_CHECK_EQUAL(frame.getROFViews().overlap.mLayerCount, 0);
126 for (int layer = 0; layer < Count::value; ++layer) {
127 const auto boundaries = frame.getROFrameClusters(layer);
128 BOOST_REQUIRE_EQUAL(boundaries.size(), 2u);
129 BOOST_CHECK_EQUAL(boundaries[0], 0);
130 BOOST_CHECK_EQUAL(boundaries[1], 0);
131 }
132
133 frame.setROFViews(views);
134 // View updates must preserve cluster boundaries, and vice versa.
135 frame.setROFViews(0, views, Count::value - 1);
136 const std::array<int, 3> boundaries{0, 0, 0};
137 frame.setROFClusters(0, boundaries);
138 BOOST_CHECK_EQUAL(frame.getROFLocalLayer(0), Count::value - 1);
139 BOOST_CHECK_EQUAL(frame.getROFViews(0).overlap.mLayerCount, Count::value);
140 frame.setROFViews(0, views, 0);
141 BOOST_CHECK_EQUAL(frame.getROFrameClusters(0).size(), boundaries.size());
142}
143
144BOOST_AUTO_TEST_CASE_TEMPLATE(MalformedInputAlwaysThrows, Count, LayerCounts)
145{
146 for (bool drop : {false, true}) {
147 Rig<Count::value> rig{drop};
148 rig.rofs[0].setNEntries(1); // Claims a missing cluster: unrecoverable InvalidROFRange.
149 int completed = 0;
150 const auto run = [&] {
151 auto cleanup = rig.session.cleanupOnExit();
152 const auto result = rig.session.process(rig.tracker, rig.traits, rig.source(), [](const o2::InteractionRecord&) {}, [&](const TrackingStatistics&) { ++completed; });
153 BOOST_CHECK(decideCATrackerPublicationAction(true, result) == CATrackerPublicationAction::SkipDroppedTimeFrame);
154 cleanup.frameAlreadyReset();
155 };
156 BOOST_CHECK_THROW(run(), std::runtime_error);
157 BOOST_CHECK_EQUAL(completed, 0);
158 rig.checkClean();
159 }
160}
161
162BOOST_AUTO_TEST_CASE_TEMPLATE(DecodingFailureAlwaysThrowsAndClearsFrame, Count, LayerCounts)
163{
164 for (bool drop : {false, true}) {
165 Rig<Count::value> rig{drop};
166 const auto run = [&] {
167 rig.session.loadWithRecovery(drop, [&] {
168 // A decoding failure after an insertion must clear partial frame data.
169 GlobalMeasurement global{};
170 global.x = 3.f;
171 global.radius = 3.f;
172 rig.session.frame.addMeasurement(LayerId{0}, global, SurfaceMeasurement{});
173 BOOST_REQUIRE_EQUAL(rig.session.frame.getTotalMeasurements(), 1u);
174 const CompClusterExt cluster{1, 1, 0, 0}; // Absent from the empty dictionary.
175 auto patterns = gsl::span<const unsigned char>{}.begin();
176 o2::itsmft::ioutils::extractClusterData(cluster, patterns, &rig.dictionary);
177 });
178 };
179 BOOST_CHECK_EXCEPTION(run(), std::runtime_error, [](const std::runtime_error& error) {
180 return std::string(error.what()).find("Cluster pattern ID is outside the topology dictionary") != std::string::npos;
181 });
182 rig.checkClean();
183 }
184}
185
186BOOST_AUTO_TEST_CASE_TEMPLATE(TimingOverflowAlwaysThrowsAndClearsFrame, Count, LayerCounts)
187{
188 for (bool drop : {false, true}) {
189 Rig<Count::value> rig{drop};
190 auto source = rig.source();
191 const o2::its::LayerTiming timing{0, 40, std::numeric_limits<uint32_t>::max(), 0, 0};
192 const auto run = [&] {
193 rig.session.loadWithRecovery(drop, [&] {
194 validateSourceROFTiming(source, {0, 0}, timing);
195 });
196 };
197 BOOST_CHECK_EXCEPTION(run(), std::runtime_error, [](const std::runtime_error& error) {
198 const std::string message = error.what();
199 return message.find("Invalid ROF timing: source=0 rof=0") != std::string::npos;
200 });
201 rig.checkClean();
202 }
203}
204
205BOOST_AUTO_TEST_CASE_TEMPLATE(StructuralLoadingAndPublicationExceptionsAlwaysCleanUp, Count, LayerCounts)
206{
207 for (bool drop : {false, true}) {
208 Rig<Count::value> rig{drop};
209 auto source = rig.source();
210 source.dictionary = nullptr;
211 const auto run = [&] {
212 auto cleanup = rig.session.cleanupOnExit();
213 rig.session.process(rig.tracker, rig.traits, source, [](const o2::InteractionRecord&) {}, [](const TrackingStatistics&) {});
214 };
215 BOOST_CHECK_THROW(run(), std::runtime_error);
216 rig.checkClean();
217 rig.configure();
218 const auto publish = [&] {
219 auto cleanup = rig.session.cleanupOnExit();
220 rig.session.process(rig.tracker, rig.traits, rig.source(), [](const o2::InteractionRecord&) {}, [](const TrackingStatistics&) { throw std::runtime_error{"publication failed"}; });
221 };
222 BOOST_CHECK_THROW(publish(), std::runtime_error);
223 rig.checkClean();
224 }
225}
226
227BOOST_AUTO_TEST_CASE_TEMPLATE(ResourceExceptionsInPostLoadHookFollowLoadingPolicy, Count, LayerCounts)
228{
229 for (bool drop : {false, true}) {
230 for (bool bounded : {false, true}) {
231 Rig<Count::value> rig{drop};
232 const auto run = [&] {
233 auto cleanup = rig.session.cleanupOnExit();
234 const auto outcome = rig.session.process(rig.tracker, rig.traits, rig.source(), [&](const o2::InteractionRecord&) {
235 if (bounded) { throw BoundedMemoryResource::MemoryLimitExceeded{2, 1, 1}; }
236 throw std::bad_alloc{}; }, [](const TrackingStatistics&) { BOOST_FAIL("must not track after failed loading"); });
237 BOOST_CHECK(!outcome);
238 cleanup.frameAlreadyReset();
239 };
240 if (drop) {
242 } else {
243 BOOST_CHECK_THROW(run(), std::bad_alloc);
244 }
245 rig.checkClean();
246 }
247 }
248}
249
250BOOST_AUTO_TEST_CASE_TEMPLATE(TrackingResourceFailureSkipsCompletionAndPublication, Count, LayerCounts)
251{
252 Rig<Count::value> rig{true, 1};
253 auto cleanup = rig.session.cleanupOnExit();
254 const auto outcome = rig.session.process(rig.tracker, rig.traits, rig.source(), [](const o2::InteractionRecord&) {}, [](const TrackingStatistics&) { BOOST_FAIL("must not complete a dropped TF"); });
255 BOOST_CHECK(!outcome);
256 BOOST_CHECK(rig.session.frame.getGenericTracks().empty());
257 cleanup.frameAlreadyReset();
258}
259
260BOOST_AUTO_TEST_CASE_TEMPLATE(TimingViewsBelongToTheSessionAndFilteringSurvivesMoves, Count, LayerCounts)
261{
262 Rig<Count::value> rig;
263 {
264 auto cleanup = rig.session.cleanupOnExit();
265 std::vector<o2::its::LayerTiming> timings(Count::value);
266 std::fill(timings.begin(), timings.end(), o2::its::LayerTiming{.mNROFsTF = 3, .mROFLength = 40});
267 rig.session.configureTiming(timings, [](int rof) { return rof != 1; });
268 std::fill(timings.begin(), timings.end(), o2::its::LayerTiming{}); // No view may refer to the caller's timing storage.
269 const auto views = rig.session.frame.getROFViews();
270 BOOST_CHECK_EQUAL(views.overlap.getLayer(0).mROFLength, 40u);
271 for (int layer = 0; layer < Count::value; ++layer) {
272 BOOST_CHECK(views.mask.isROFEnabled(layer, 0));
273 BOOST_CHECK(!views.mask.isROFEnabled(layer, 1));
274 BOOST_CHECK(views.mask.isROFEnabled(layer, 2));
275 }
276 std::fill(timings.begin(), timings.end(), o2::its::LayerTiming{.mNROFsTF = 3, .mROFLength = 40});
277 timings[1].mROFLength = 41;
278 BOOST_CHECK_THROW(rig.session.configureTiming(timings, [](int) { return true; }), std::runtime_error);
279 BOOST_CHECK_EQUAL(rig.session.frame.getROFViews().overlap.getLayer(1).mROFLength, 40u);
280 }
281 rig.checkClean();
282}
283
284BOOST_AUTO_TEST_CASE_TEMPLATE(InactivePublicationRetainsTheEchoedEmptyContract, Count, LayerCounts)
285{
286 for (auto outcome : {true, false}) {
287 BOOST_CHECK(decideCATrackerPublicationAction(false, outcome) == CATrackerPublicationAction::PublishInactiveEmpty);
288 }
289}
290
291BOOST_AUTO_TEST_CASE_TEMPLATE(UnclassifiedExceptionsDoNotBecomeRecoverableDrops, Count, LayerCounts)
292{
293 for (bool drop : {false, true}) {
294 for (bool standard : {false, true}) {
295 Rig<Count::value> rig{drop};
296 const auto run = [&] {
297 auto cleanup = rig.session.cleanupOnExit();
298 rig.session.process(rig.tracker, rig.traits, rig.source(), [&](const o2::InteractionRecord&) {
299 if (standard) { throw std::logic_error{"unexpected loading failure"}; }
300 throw 7; }, [](const TrackingStatistics&) { BOOST_FAIL("must not complete after an exception"); });
301 };
302 if (standard) {
303 BOOST_CHECK_THROW(run(), std::logic_error);
304 } else {
305 BOOST_CHECK_THROW(run(), int);
306 }
307 rig.checkClean();
308 }
309 }
310}
311
312namespace
313{
314struct TestOutputAllocator {
315 std::map<int, std::any> values;
316 template <typename Vector, typename Iterator>
317 Vector& make(int output, Iterator first, Iterator last)
318 {
319 values[output] = Vector(first, last);
320 return std::any_cast<Vector&>(values.at(output));
321 }
322};
323} // namespace
324BOOST_AUTO_TEST_CASE_TEMPLATE(PublishedCommonColumnsOwnTheirStorageAfterSessionCleanup, Count, LayerCounts)
325{
326 struct Staged {
327 std::vector<GenericTrack> tracks;
328 std::vector<ROFRecord> trackROFs;
329 std::vector<int> clusterIndices;
330 };
331 Rig<Count::value> rig;
332 TestOutputAllocator outputs;
333 {
334 auto cleanup = rig.session.cleanupOnExit();
335 Staged staged;
336 staged.tracks.resize(1);
337 staged.trackROFs.emplace_back(o2::InteractionRecord{123, 45}, 0, 0, 1);
338 staged.clusterIndices = {17, 23};
339 copyTrackingOutputColumns(outputs, 0, 1, 2, staged);
340 staged.clusterIndices[0] = 99;
341 staged.trackROFs[0].setNEntries(0);
342 staged.tracks.clear();
343 }
344 rig.checkClean();
345 const auto& rofs = std::any_cast<const std::vector<ROFRecord>&>(outputs.values.at(0));
346 BOOST_REQUIRE_EQUAL(rofs.size(), 1u);
347 BOOST_CHECK_EQUAL(rofs[0].getNEntries(), 1);
348 BOOST_CHECK((rofs[0].getBCData() == o2::InteractionRecord{123, 45}));
349 BOOST_CHECK_EQUAL(std::any_cast<const decltype(Staged{}.tracks)&>(outputs.values.at(1)).size(), 1u);
350 const auto& indices = std::any_cast<const std::vector<int>&>(outputs.values.at(2));
351 const std::vector<int> expected{17, 23};
352 BOOST_CHECK_EQUAL_COLLECTIONS(indices.begin(), indices.end(), expected.begin(), expected.end());
353}
354
355BOOST_AUTO_TEST_CASE_TEMPLATE(DetectorTimingConstructionRetainsValidationAndUnits, Count, LayerCounts)
356{
357 struct AlpideTiming {
358 int length = 40;
359 int getROFLengthInBC(int) const { return length; }
360 int getROFDelayInBC(int) const { return 3; }
361 int getROFBiasInBC(int) const { return 4; }
362 } alpide;
363 Rig<Count::value> rig;
364 const std::vector<uint32_t> timeErrors(Count::value, 5);
365 const auto timings = rig.session.layerTimings(alpide, 2, timeErrors);
366 for (const auto& timing : timings) {
367 BOOST_CHECK_EQUAL(timing.mROFLength, 40u);
368 BOOST_CHECK_EQUAL(timing.mROFDelay, 3u);
369 BOOST_CHECK_EQUAL(timing.mROFBias, 4u);
370 BOOST_CHECK_EQUAL(timing.mROFAddTimeErr, 5u);
371 BOOST_CHECK_EQUAL(timing.mNROFsTF, 178u);
372 }
373 BOOST_CHECK_EXCEPTION(rig.session.layerTimings(alpide, 0, timeErrors), std::runtime_error,
374 [](const std::runtime_error& error) { return std::string(error.what()).find("zero ROFs") != std::string::npos; });
375 BOOST_CHECK_THROW(rig.session.layerTimings(alpide, 2, std::vector<uint32_t>(Count::value - 1)), std::runtime_error);
376 alpide.length = 0;
377 BOOST_CHECK_EXCEPTION(rig.session.layerTimings(alpide, 2, timeErrors), std::runtime_error,
378 [](const std::runtime_error& error) { return std::string(error.what()).find("non-positive ROF length") != std::string::npos; });
379}
380
381BOOST_AUTO_TEST_CASE_TEMPLATE(UnchangedTimingReusesStorageButRefreshesEventData, Count, LayerCounts)
382{
383 WorkflowSession session{"test", Count::value};
384 std::vector<o2::its::LayerTiming> timings(Count::value);
385 std::fill(timings.begin(), timings.end(), o2::its::LayerTiming{.mNROFsTF = 3, .mROFLength = 40});
386 session.configureTiming(timings, [](int rof) { return rof == 0; });
388 vertex.getTimeStamp().setTimeStamp(20);
389 vertex.getTimeStamp().setTimeStampError(5);
390 session.vertices.update(&vertex, 1);
391 const auto overlapStorage = session.overlap.getView().mFlatTable;
392 const auto vertexStorage = session.vertices.getView().mFlatTable;
393 const auto maskStorage = session.mask.getView().mFlatMask;
394 BOOST_REQUIRE_EQUAL(session.vertices.getView().getVertices(0, 0).getEntries(), 1u);
395 session.reset();
396 session.invalidatePublication();
397 BOOST_CHECK_EQUAL(session.frame.getROFViews().overlap.mLayerCount, 0);
398
399 int calls = 0;
400 session.configureTiming(timings, [&](int rof) { ++calls; return rof == 2; });
401 std::fill(timings.begin(), timings.end(), o2::its::LayerTiming{}); // The key and table definitions own their timing values.
402 BOOST_CHECK_EQUAL(calls, 3);
403 BOOST_CHECK(session.overlap.getView().mFlatTable == overlapStorage);
404 BOOST_CHECK(session.vertices.getView().mFlatTable == vertexStorage);
405 BOOST_CHECK(session.mask.getView().mFlatMask == maskStorage);
406 for (int layer = 0; layer < Count::value; ++layer) {
407 for (int rof = 0; rof < 3; ++rof) {
408 const auto range = session.vertices.getView().getVertices(layer, rof);
409 BOOST_CHECK_EQUAL(range.getFirstEntry(), 0u);
410 BOOST_CHECK_EQUAL(range.getEntries(), 0u);
411 BOOST_CHECK_EQUAL(session.frame.getROFViews().mask.isROFEnabled(layer, rof), rof == 2);
412 }
413 }
414 // New truth contents can be bound after a cache hit, then cleared again.
415 vertex.getTimeStamp().setTimeStamp(100);
416 session.vertices.update(&vertex, 1);
417 BOOST_CHECK_EQUAL(session.vertices.getView().getVertices(0, 2).getEntries(), 1u);
418 std::fill(timings.begin(), timings.end(), o2::its::LayerTiming{.mNROFsTF = 3, .mROFLength = 40});
419 session.configureTiming(timings, [](int) { return false; });
420 BOOST_CHECK_EQUAL(session.vertices.getView().getVertices(0, 2).getEntries(), 0u);
421 BOOST_CHECK(!session.frame.getROFViews().mask.isROFEnabled(0, 2));
422}
423
424BOOST_AUTO_TEST_CASE_TEMPLATE(EveryTimingFieldAndLayerExtentInvalidateTheCache, Count, LayerCounts)
425{
426 using Timing = o2::its::LayerTiming;
427 constexpr std::array fields{&Timing::mNROFsTF, &Timing::mROFLength, &Timing::mROFDelay,
428 &Timing::mROFBias, &Timing::mROFAddTimeErr};
429 std::vector<Timing> baseline(Count::value);
430 std::fill(baseline.begin(), baseline.end(), Timing{.mNROFsTF = 3, .mROFLength = 40});
431 WorkflowSession session{"test", Count::value};
432 const auto accept = [](int rof) { return rof < 3 && rof != 1; };
433 const auto compareWithFresh = [&](const auto& timings) {
434 session.configureTiming(timings, accept);
435 WorkflowSession fresh{"oracle", Count::value};
436 fresh.configureTiming(timings, accept);
437 const auto actual = session.overlap.getView();
438 const auto expected = fresh.overlap.getView();
439 for (int layer = 0; layer < Count::value; ++layer) {
440 for (auto field : fields) {
441 BOOST_CHECK_EQUAL(actual.getLayer(layer).*field, timings[layer].*field);
442 BOOST_CHECK_EQUAL(session.vertices.getView().getLayer(layer).*field, timings[layer].*field);
443 }
444 for (uint32_t rof = 0; rof < timings[layer].mNROFsTF; ++rof) {
445 BOOST_CHECK_EQUAL(session.mask.getView().isROFEnabled(layer, rof), fresh.mask.getView().isROFEnabled(layer, rof));
446 BOOST_CHECK_EQUAL(session.vertices.getView().getVertices(layer, rof).getEntries(), 0u);
447 for (int to = 0; to < Count::value; ++to) {
448 if (layer == to) {
449 continue;
450 }
451 BOOST_CHECK_EQUAL(actual.getOverlap(layer, to, rof).getFirstEntry(), expected.getOverlap(layer, to, rof).getFirstEntry());
452 BOOST_CHECK_EQUAL(actual.getOverlap(layer, to, rof).getEntries(), expected.getOverlap(layer, to, rof).getEntries());
453 }
454 }
455 }
456 };
457 for (auto field : fields) {
458 compareWithFresh(baseline);
459 auto changed = baseline;
460 for (auto& timing : changed) {
461 timing.*field += 1;
462 }
463 compareWithFresh(changed);
464 compareWithFresh(changed); // Reuse must match the fresh oracle as well.
465 compareWithFresh(baseline); // Includes shrinking the TF again.
466 if (field != &Timing::mNROFsTF) {
467 for (int layer = 0; layer < Count::value; ++layer) {
468 auto nonuniform = baseline;
469 nonuniform[layer].*field += 1;
470 BOOST_CHECK_THROW(session.configureTiming(nonuniform, accept), std::runtime_error);
471 }
472 }
473 }
474 // Uniformity constrains the four BC fields, but each layer has its own extent.
475 for (int layer = 0; layer < Count::value; ++layer) {
476 auto changed = baseline;
477 changed[layer].mNROFsTF += 1;
478 compareWithFresh(changed);
479 compareWithFresh(baseline);
480 }
481}
482
483BOOST_AUTO_TEST_CASE_TEMPLATE(FilterFailureLeavesNoEventViewsAndDoesNotPoisonTimingReuse, Count, LayerCounts)
484{
485 WorkflowSession session{"test", Count::value};
486 std::vector<o2::its::LayerTiming> timings(Count::value);
487 std::fill(timings.begin(), timings.end(), o2::its::LayerTiming{.mNROFsTF = 3, .mROFLength = 40});
488 session.configureTiming(timings, [](int) { return true; });
489 for (bool changeTiming : {false, true}) {
490 if (changeTiming) {
491 for (auto& timing : timings) {
492 timing.mROFLength += 1;
493 }
494 }
495 BOOST_CHECK_THROW(session.configureTiming(timings, [](int rof) {
496 if (rof == 1) {
497 throw std::runtime_error{"filter failed"};
498 }
499 return true;
500 }),
501 std::runtime_error);
502 BOOST_CHECK_EQUAL(session.frame.getROFViews().overlap.mLayerCount, 0);
503 const auto storage = session.overlap.getView().mFlatTable;
504 session.configureTiming(timings, [](int rof) { return rof == 2; });
505 BOOST_CHECK(session.overlap.getView().mFlatTable == storage);
506 for (int layer = 0; layer < Count::value; ++layer) {
507 BOOST_CHECK(!session.frame.getROFViews().mask.isROFEnabled(layer, 0));
508 BOOST_CHECK(!session.frame.getROFViews().mask.isROFEnabled(layer, 1));
509 BOOST_CHECK(session.frame.getROFViews().mask.isROFEnabled(layer, 2));
510 BOOST_CHECK_EQUAL(session.vertices.getView().getVertices(layer, 0).getEntries(), 0u);
511 }
512 }
513}
514
515BOOST_AUTO_TEST_CASE_TEMPLATE(InvalidTimingLayerCountPreservesCachedConfiguration, Count, LayerCounts)
516{
517 WorkflowSession session{"test", Count::value};
518 std::vector<o2::its::LayerTiming> timings(Count::value, {.mNROFsTF = 3, .mROFLength = 40});
519 const auto accept = [](int) { return true; };
520 session.configureTiming(timings, accept);
521 const auto cached = session.overlap.getView().mFlatTable;
522 for (auto count : {0, Count::value - 1, Count::value + 1}) {
523 auto invalid = timings;
524 invalid.resize(count, timings.front());
525 BOOST_CHECK_THROW(session.configureTiming(invalid, accept), std::runtime_error);
526 BOOST_CHECK(session.frame.getROFViews().overlap.mFlatTable == cached);
527 }
528 session.configureTiming(timings, accept);
529 BOOST_CHECK(session.overlap.getView().mFlatTable == cached);
530}
header::DataOrigin origin
std::vector< std::shared_ptr< arrow::Field > > fields
uint64_t vertex
Definition RawEventData.h:9
bool publish(std::string const &filename, std::string const &path, std::string CCDBpath)
Definition GRPTool.cxx:199
std::vector< o2::itsmft::ROFRecord > trackROFs
std::vector< int > clusterIndices
std::vector< o2::its::TrackITS > tracks
void output(const std::map< std::string, ChannelStat > &channels)
Definition rawdump.cxx:197
static int initFieldFromGRP(const o2::parameters::GRPMagField *grp, bool verbose=false)
void setNThreads(int n, std::shared_ptr< tbb::task_arena > &arena)
bool initialize(TimeFrame &frame, const TrackerInitialization &configuration)
Definition Tracker.cxx:414
void configureTiming(gsl::span< const o2::its::LayerTiming > timings, AcceptROF &&accept)
GLint GLsizei count
Definition glcorearb.h:399
GLuint64EXT * result
Definition glcorearb.h:5662
GLint first
Definition glcorearb.h:399
GLsizei GLsizei GLchar * source
Definition glcorearb.h:798
GLenum GLint * range
Definition glcorearb.h:1899
GLenum GLsizei GLsizei GLint * values
Definition glcorearb.h:1576
GLuint GLsizei GLsizei * length
Definition glcorearb.h:790
GLsizei GLenum const void * indices
Definition glcorearb.h:400
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLuint GLsizei const GLchar * message
Definition glcorearb.h:2517
GLuint memory
Definition glcorearb.h:5234
o2::itsmft::tracking::LayerTiming LayerTiming
ClusterData< T > extractClusterData(const CompClusterExt &c, gsl::span< const unsigned char >::iterator &patterns, const TopologyDictionary *dict)
Definition IOUtils.h:81
void copyTrackingOutputColumns(Allocator &outputs, Output rofs, Output tracks, Output indices, const Staged &staged)
void validateSourceROFTiming(const ClusterSourceInput &source, const o2::InteractionRecord &origin, const o2::its::LayerTiming &timing)
constexpr std::array< SurfaceDescriptor, MFTNLayers > kMFTSurfaces
void loadTimeFrameSources(TimeFrame &, gsl::span< const ClusterSourceInput >, SurfaceCatalogView, std::vector< std::vector< uint32_t > > *externalIndicesBySurface=nullptr, std::vector< std::vector< uint32_t > > *clusterSizesBySurface=nullptr)
Definition IOUtils.cxx:337
constexpr int MFTNLayers
MFT CA half-disk layer count.
constexpr std::array< SurfaceDescriptor, ITSNLayers > kITSSurfaces
constexpr int ITSNLayers
ITS CA layer count.
CATrackerPublicationAction decideCATrackerPublicationAction(bool active, bool success) noexcept
constexpr auto make(F f) -> std::array< int, N >
Definition SMatrixGPU.h:293
void cleanup()
const o2::itsmft::TopologyDictionary * dictionary
Definition IOUtils.h:124
gsl::span< const o2::itsmft::CompClusterExt > clusters
Definition IOUtils.h:121
o2::detectors::DetID::ID detector
Definition IOUtils.h:120
gsl::span< const o2::itsmft::ROFRecord > rofs
Definition IOUtils.h:123
gsl::span< const LayerId > layerToSurface
Definition IOUtils.h:126
BOOST_GLOBAL_FIXTURE(Fixture)
std::map< std::string, ID > expected
boost::mpl::list< std::integral_constant< int, ITSNLayers >, std::integral_constant< int, MFTNLayers > > LayerCounts
BOOST_AUTO_TEST_CASE_TEMPLATE(ValidEmptyInputCompletesBeforeCleanup, Count, LayerCounts)
BOOST_CHECK_NO_THROW(algorithm::merge(target, other))
BOOST_CHECK(tree)
std::vector< Cluster > clusters
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())