Project
Loading...
Searching...
No Matches
testTrackerFailureContract.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// Tracker failure contract: Tracker::run()
13// exception classification, cleanup after tracking failures, and boolean success.
14//
15// Contract under test (see Tracker.h/Tracker.cxx):
16// - std::invalid_argument (structural/configuration failure): TimeFrame is
17// wiped, then the exception always rethrows, regardless of
18// DropTFUponFailure.
19// - BoundedMemoryResource::MemoryLimitExceeded
20// (recoverable, per-TF resource failures): TimeFrame is wiped;
21// DropTFUponFailure=true returns false, DropTFUponFailure=false rethrows.
22// - Valid empty input (a real layout/topology with zero loaded clusters)
23// returns true and records a positive elapsed time.
24// - A tracker instance that dropped one TimeFrame can immediately process a
25// following one successfully.
26//
27// Every fixture below establishes a real layout/plan and selected workspace
28// and then loads a normalized source -- even the structural-failure cases,
29// and even when that source carries zero clusters/ROFs -- before running
30// tracking. This is load-bearing, not incidental: TimeFrame::initialise()
31// unconditionally calls getNrof(layer) = mROFramesClusters[layer].size()-1
32// on every layer, and a never-loaded (default-constructed, size-0)
33// mROFramesClusters underflows that subtraction, corrupting memory deep
34// inside prepareClusters() rather than throwing a clean exception.
35// loadNormalizedSource() sizes mROFramesClusters[layer] to rofs.size()+1 for
36// every layer regardless of whether clusters/rofs are empty, which is what
37// makes that call, and every "iterate 0..getNrof()" loop reached afterward,
38// safe. The structural-failure cases below produce their std::invalid_argument
39// through an invalid TrackingParameters/index-table configuration, not
40// through a missing/stale plan: Gate 4 B2 Slice 2 removed the plan-currency
41// concept entirely (initialiseTimeFrame() now takes the plan as an explicit
42// layout/topology view parameter, so "no plan" is no longer a state a
43// caller can even construct) -- see the removed
44// StructuralFailureViaStaleLayoutAlwaysRethrowsAndWipes test's replacement
45// note below for what covers the "always rethrows and wipes" contract now.
46//
47// The recoverable-failure fixtures tighten the already-used frame allocator
48// to its current usage. The next tracking allocation then exercises the
49// normal bounded-resource failure/reset contract without changing config.
50
51#define BOOST_TEST_MODULE ITSMFT Tracker failure contract
52#define BOOST_TEST_MAIN
53#define BOOST_TEST_DYN_LINK
55#include <boost/test/unit_test.hpp>
56
57#include <limits>
58#include <memory>
59#include <optional>
60#include <stdexcept>
61#include <vector>
62
63#include <gsl/gsl>
64#include <oneapi/tbb/task_arena.h>
65
66#include <TGeoGlobalMagField.h>
67#include "Field/MagneticField.h"
68
86
87using namespace o2::itsmft;
88using namespace o2::itsmft::tracking;
89
90namespace
91{
92
93// Deterministic, geometry-free stand-in for detector geometry decoding,
94// identical construction to testTimeFrameLifecycle.cxx /
95// testTimeFrameNormalizedSource.cxx / testMultiSourceLoading.cxx.
96class LegacyLikeDecoder
97{
98 public:
99 explicit LegacyLikeDecoder(o2::detectors::DetID::ID detector) : mDetector(detector) {}
100
102 const CompClusterExt& cluster,
103 gsl::span<const unsigned char>::iterator& patterns,
104 const TopologyDictionary* dict,
105 uint32_t) const
106 {
107 const auto clusterData = o2::itsmft::ioutils::extractClusterData(cluster, patterns, dict);
109 const int sensorID = cluster.getSensorID();
110 auto& decoded = result;
111 decoded.global = {static_cast<float>(sensorID) * 10.f, static_cast<float>(cluster.getRow()), static_cast<float>(cluster.getCol())};
112 decoded.cylinderFrame = {static_cast<float>(sensorID) + 100.f, static_cast<float>(cluster.getRow()) + 1.f, static_cast<float>(cluster.getCol()) + 2.f, 0.01f * sensorID};
113 decoded.rowColumnCovariance = {clusterData.sig2Row, 0.f, clusterData.sig2Col};
114 decoded.nPixels = clusterData.nPixels;
115 decoded.layer = sensorID;
116 return result;
117 }
118
119 private:
120 o2::detectors::DetID::ID mDetector;
121};
122
123const TopologyDictionary& dict()
124{
125 static const TopologyDictionary d;
126 return d;
127}
128
129// TrackerTraits::findRoads() unconditionally touches the global
130// o2::base::Propagator singleton on first use, which in turn requires
131// TGeoGlobalMagField to already hold a real o2::field::MagneticField
132// object -- with none set (the state of every other test in this suite,
133// none of which calls Tracker::run() end to end), Propagator falls
134// back to a legacy FairRunAna singleton that also does not exist in this
135// process and segfaults dereferencing it. Only the tests that expect a
136// genuinely successful Tracker::run() (valid empty input,
137// continued processing after a drop) reach findRoads(); the
138// structural/recoverable-failure tests throw/return before ever getting
139// there and do not need this. A trivial default-constructed
140// MagneticField (no field map file, zero solenoid current) is sufficient
141// -- these tests never fit or propagate an actual trajectory since there
142// are no clusters. TGeoGlobalMagField::Instance()->Lock() only allows one
143// SetField() call per process, so this must run at most once.
144void ensureTrivialMagneticFieldIsSet()
145{
146 static const bool done = [] {
147 TGeoGlobalMagField::Instance()->SetField(new o2::field::MagneticField());
148 TGeoGlobalMagField::Instance()->Lock();
149 return true;
150 }();
151 (void)done;
152}
153
154constexpr std::array<unsigned char, 3> onePixelPattern{1, 1, 0x80};
155constexpr std::array<unsigned char, 3> threePixelPattern{1, 3, 0xE0};
156
157std::vector<unsigned char> concatPatterns(std::initializer_list<gsl::span<const unsigned char>> parts)
158{
159 std::vector<unsigned char> bytes;
160 for (const auto& p : parts) {
161 bytes.insert(bytes.end(), p.begin(), p.end());
162 }
163 return bytes;
164}
165
166std::vector<SurfaceDescriptor> makeITSTestCatalog()
167{
168 std::vector<SurfaceDescriptor> surfaces;
169 surfaces.reserve(ITSNLayers);
170 for (uint16_t i = 0; i < ITSNLayers; ++i) {
171 surfaces.push_back(SurfaceDescriptor{i, static_cast<uint8_t>(o2::detectors::DetID::ITS), SurfaceKind::Cylinder});
172 surfaces.back().referenceCoordinate = kITSSurfaces[i].referenceCoordinate;
173 surfaces.back().chartRange = {-20.f, 20.f};
174 // Use the material from the detector surface catalog.
175 const float xOverX0 = kITSSurfaces[i].material.xOverX0;
176 surfaces.back().material.xOverX0 = xOverX0;
177 surfaces.back().material.arealDensityGPerCm2 = xOverX0 * o2::its::constants::Radl * o2::its::constants::Rho;
178 }
179 return surfaces;
180}
181
182std::vector<LayerId> identitySurfaces(uint16_t nLayers)
183{
184 std::vector<LayerId> mapping;
185 mapping.reserve(nLayers);
186 for (uint16_t i = 0; i < nLayers; ++i) {
187 mapping.push_back(LayerId{i});
188 }
189 return mapping;
190}
191
192struct Fixture {
193 std::vector<CompClusterExt> clusters;
194 std::vector<unsigned char> patterns;
195 std::vector<ROFRecord> rofs;
197};
198
199// 4 clusters on layers {0,1,0,2}, partitioned into 3 ROFs. Same shape as
200// testTimeFrameLifecycle.cxx's fixture -- only needed to give the
201// recoverable-failure fixture genuine per-event content to wipe.
202Fixture makeFixture()
203{
204 Fixture f;
205 f.clusters = {
210 };
211 f.patterns = concatPatterns({onePixelPattern, threePixelPattern, onePixelPattern, threePixelPattern});
212 f.rofs = {
213 ROFRecord{{100, 5}, 0, 0, 2},
214 ROFRecord{{140, 5}, 1, 2, 1},
215 ROFRecord{{1000, 6}, 2, 3, 1}};
216 for (uint32_t i = 0; i < f.clusters.size(); ++i) {
217 f.labels.addElement(i, o2::MCCompLabel{static_cast<int>(i) + 1, 0, 0});
218 }
219 return f;
220}
221
222std::vector<TrackingParameters> makeOneIterationITSParams(bool dropTFUponFailure, size_t maxMemory = std::numeric_limits<size_t>::max())
223{
224 std::vector<TrackingParameters> params(1);
225 params[0] = test::makeTestTrackingParameters(o2::detectors::DetID::ITS);
226 params[0].DropTFUponFailure = dropTFUponFailure;
227 params[0].MaxMemory = maxMemory;
228 return params;
229}
230
231// A valid FirstPass iteration 0 followed by a non-FirstPass (RebuildClusterLUT
232// only, matching the legacy ITS async-iteration-3 shape) iteration 1, both ITS
233// defaults -- callers mutate params[1]'s index-table fields to construct a
234// deliberate mismatch against the configuration iteration 0 will commit.
235std::vector<TrackingParameters> makeTwoIterationITSParams(bool dropTFUponFailure)
236{
237 std::vector<TrackingParameters> params(2);
238 params[0] = test::makeTestTrackingParameters(o2::detectors::DetID::ITS);
239 params[1] = test::makeTestTrackingParameters(o2::detectors::DetID::ITS);
240 params[1].PassFlags = IterationSteps{IterationStep::RebuildClusterLUT};
241 for (auto& p : params) {
242 p.DropTFUponFailure = dropTFUponFailure;
243 }
244 return params;
245}
246
247// Bundles a TimeFrame, real backend, Tracker, and bounded memory pool -- the
248// minimal wiring Tracker::run() needs for the ITS configuration tests below.
249struct Rig {
250 explicit Rig(bool dropTFUponFailure, size_t maxMemory = std::numeric_limits<size_t>::max())
251 : pool(std::make_shared<BoundedMemoryResource>()),
252 params(makeOneIterationITSParams(dropTFUponFailure, maxMemory)),
253 tracker()
254 {
255 traits.setNThreads(1, arena);
256 frame.setBz(0.5f);
257 }
258
259 // Stage a GenericTrack and its reference to exercise resetTimeFrame().
260 void stageStaleState()
261 {
262 frame.getTrackClusterIndices().push_back(TrackClusterReference{LayerId{0}, 0, 0});
263 GenericTrack track{};
264 track.clusterRefEnd = static_cast<uint32_t>(frame.getTrackClusterIndices().size());
265 frame.getGenericTracks().push_back(track);
266 BOOST_REQUIRE(!frame.getGenericTracks().empty());
267 BOOST_REQUIRE(!frame.getTrackClusterIndices().empty());
268 }
269
270 std::shared_ptr<BoundedMemoryResource> pool;
271 std::vector<TrackingParameters> params;
272 TimeFrame frame;
273 TrackerTraits traits;
274 Tracker tracker;
275 // Scratch carries non-owning runtime ROF views. Keep these adapter-edge
276 // builders alive across load, initialise, and failure/replacement calls.
277 std::optional<o2::its::ROFOverlapTable<ITSNLayers>> rofTable;
278 std::optional<o2::its::ROFVertexLookupTable<ITSNLayers>> vertexTable;
279 std::optional<o2::its::ROFMaskTable<ITSNLayers>> mask;
280 std::shared_ptr<tbb::task_arena> arena;
281 std::vector<SurfaceDescriptor> catalog;
282
283 // Builds and atomically installs the complete static configuration.
284 void establishValidLayout()
285 {
286 catalog = makeITSTestCatalog();
287 const SurfaceCatalogView catalogView{catalog.data(), static_cast<uint32_t>(catalog.size())};
288 TrackerInitialization configuration;
289 configuration.catalog = catalogView;
290 configuration.memoryPool = pool;
291 const auto orderedSurfaces = identitySurfaces(ITSNLayers);
293 const auto result = tracker.initialize(frame, configuration);
294 BOOST_REQUIRE(result);
295 BOOST_REQUIRE_EQUAL(frame.getDetectorConfiguration().size(), orderedSurfaces.size());
296 }
297
298 // Loads clusters (or, with an empty Fixture, zero clusters -- still a
299 // valid load that sizes every per-layer ROF boundary table to a real,
300 // if trivial, state) through the same normalized-loading path production
301 // code uses. This sizing is load-bearing: TimeFrame::initialise() calls
302 // getNrof(layer) = mROFramesClusters[layer].size() - 1 unconditionally,
303 // and a never-loaded (default-constructed, size-0) mROFramesClusters
304 // underflows that subtraction, crashing deep inside prepareClusters()
305 // before any failure-contract check ever runs. loadNormalizedSource()
306 // sizes mROFramesClusters[layer] to rofs.size()+1 for every layer even
307 // when rofs/clusters are empty, so calling it with an empty Fixture is
308 // the only proven-safe way to reach a genuinely valid, still-empty
309 // TimeFrame state.
310 void loadSource(const Fixture& f)
311 {
312 LegacyLikeDecoder decoder{o2::detectors::DetID::ITS};
313 const o2::InteractionRecord origin{50, 5};
314 const o2::its::LayerTiming timing{.mROFLength = 40};
315 const auto& layout = frame.getDetectorConfiguration();
316 const auto layerMapping = identitySurfaces(ITSNLayers);
317 BOOST_REQUIRE_NO_THROW(test::loadTimeFrameSource(frame, decoder, origin, timing, f.clusters, f.patterns, f.rofs, &dict(),
318 f.labels.getIndexedSize() > 0 ? &f.labels : nullptr, o2::detectors::DetID::ITS,
319 gsl::span<const LayerId>{layerMapping}, layout.getSurfaceCatalog()));
320
321 // TrackerTraits::computeLayerTracklets() reads per-layer ROF counts
322 // from mROFOverlapTableView (o2::its::LayerTiming), a separate table
323 // from mROFramesClusters/getNrof() -- it is never populated by
324 // loadNormalizedSource() and defaults to an unconfigured/garbage view.
325 // A traversal that reaches computeLayerTracklets() without this being
326 // set derives its ROF loop bound from that garbage view and walks out
327 // of bounds. Mirrors the workflow timing-table construction's
328 // shape, but with every layer given the same trivial timing matching
329 // this fixture's single combined ROF stream (real production input has
330 // per-detector-param ROF length/delay/bias; none of that is exercised
331 // by the failure-contract cases here, only the ROF *count* is load
332 // -bearing).
333 o2::its::LayerTiming timing2{};
334 timing2.mNROFsTF = static_cast<unsigned int>(f.rofs.size());
335 timing2.mROFLength = 40;
336 rofTable.emplace();
337 for (int iLayer = 0; iLayer < ITSNLayers; ++iLayer) {
338 rofTable->defineLayer(iLayer, timing2);
339 }
340 rofTable->init();
341 vertexTable.emplace();
342 for (int iLayer = 0; iLayer < ITSNLayers; ++iLayer) {
343 vertexTable->defineLayer(iLayer, timing2);
344 }
345 vertexTable->init();
346
347 mask.emplace(*rofTable);
348 mask->resetMask();
349 for (int iLayer = 0; iLayer < ITSNLayers; ++iLayer) {
350 mask->setROFsEnabled(iLayer, 0, timing2.mNROFsTF, 1);
351 }
352 frame.setROFViews(RuntimeROFViews{rofTable->getView(), vertexTable->getView(), mask->getView(), {}});
353 }
354
355 // Set the event-local budget at the current usage; the next allocation is
356 // the controlled recoverable failure.
357 void forceMemoryLimitAtCurrentUsage()
358 {
359 const auto used = pool->getUsedMemory();
360 pool->setMaxMemory(used);
361 }
362
363 void restoreUnboundedMemory()
364 {
365 pool->setMaxMemory(std::numeric_limits<size_t>::max());
366 }
367};
368
369Fixture emptyFixture()
370{
371 return Fixture{};
372}
373
374} // namespace
375
376// --- Recoverable failure: DropTFUponFailure decides, always wipes --------
377
378BOOST_AUTO_TEST_CASE(StructuralFailureAlwaysRethrowsAndResetsTimeFrame)
379{
380 ensureTrivialMagneticFieldIsSet();
381 for (const bool dropFlag : {false, true}) {
382 Rig rig{dropFlag};
383 rig.establishValidLayout();
384 rig.loadSource(makeFixture());
385 rig.stageStaleState();
386 auto measurements = rig.frame.getGlobalMeasurements(LayerId{0});
387 BOOST_REQUIRE(!measurements.empty());
388 measurements.front().clusterId = std::numeric_limits<uint32_t>::max();
389
390 BOOST_CHECK_THROW(rig.tracker.run(rig.frame, rig.traits), std::invalid_argument);
391 BOOST_CHECK_EQUAL(rig.frame.getTotalMeasurements(), 0u);
392 BOOST_CHECK(rig.frame.getGenericTracks().empty());
393 BOOST_CHECK(rig.frame.getTrackClusterIndices().empty());
394 }
395}
396
397BOOST_AUTO_TEST_CASE(RecoverableFailureDroppedReturnsFalseAndWipes)
398{
399 Rig rig{/*dropTFUponFailure=*/true};
400 rig.establishValidLayout();
401 rig.loadSource(makeFixture());
402 BOOST_REQUIRE(rig.frame.getTotalMeasurements() > 0u);
403
404 rig.forceMemoryLimitAtCurrentUsage();
405
406 const auto result = rig.tracker.run(rig.frame, rig.traits);
407
409 BOOST_CHECK_EQUAL(rig.frame.getTotalMeasurements(), 0u);
410 BOOST_CHECK(rig.frame.getGenericTracks().empty());
411}
412
413BOOST_AUTO_TEST_CASE(RecoverableFailureNotDroppedRethrowsButStillWipesFirst)
414{
415 Rig rig{/*dropTFUponFailure=*/false};
416 rig.establishValidLayout();
417 rig.loadSource(makeFixture());
418 BOOST_REQUIRE(rig.frame.getTotalMeasurements() > 0u);
419
420 rig.forceMemoryLimitAtCurrentUsage();
421
422 BOOST_CHECK_THROW(rig.tracker.run(rig.frame, rig.traits), BoundedMemoryResource::MemoryLimitExceeded);
423
424 // Wipe must have already happened before the exception propagated -- not
425 // "the process is going down anyway".
426 BOOST_CHECK_EQUAL(rig.frame.getTotalMeasurements(), 0u);
427 BOOST_CHECK(rig.frame.getGenericTracks().empty());
428}
429
430// --- std::bad_alloc: recoverable, same drop-or-rethrow policy ------------
431//
432// A real ten-disk MFT event exercises Tracker::run() while a test-owned
433// upstream resource injects the plain-heap failure category.
434
435// --- Index-table configuration failures: structural, always rethrow -------
436//
437BOOST_AUTO_TEST_CASE(InvalidIndexTableConfigurationIsRejectedBeforeTimeFrameConfiguration)
438{
439 for (const bool dropFlag : {false, true}) {
440 Rig rig{dropFlag};
441 rig.params[0].RowBins = 0; // structurally invalid
442 rig.catalog = makeITSTestCatalog();
443 const auto orderedSurfaces = identitySurfaces(ITSNLayers);
444 TrackerInitialization configuration;
445 configuration.catalog = {rig.catalog.data(), static_cast<uint32_t>(rig.catalog.size())};
446 configuration.memoryPool = rig.pool;
447 configuration.plan = o2::itsmft::tracking::test::makeTrackingPlan(rig.params);
448 const auto result = rig.tracker.initialize(rig.frame, configuration);
450 BOOST_CHECK(!rig.frame.isConfigured());
451 }
452}
453
454BOOST_AUTO_TEST_CASE(IterationSpecificInvalidKernelIsRejectedBeforeCommit)
455{
456 for (const bool dropFlag : {false, true}) {
457 Rig rig{dropFlag};
458 rig.params = makeTwoIterationITSParams(dropFlag);
459 const auto validMinPt = rig.params[1].TrackletMinPt;
460 rig.params[1].TrackletMinPt = -1.f;
461 rig.catalog = makeITSTestCatalog();
462 TrackerInitialization configuration;
463 configuration.catalog = {rig.catalog.data(), static_cast<uint32_t>(rig.catalog.size())};
464 configuration.memoryPool = rig.pool;
465 configuration.plan = o2::itsmft::tracking::test::makeTrackingPlan(rig.params);
466 const auto result = rig.tracker.initialize(rig.frame, configuration);
468 BOOST_CHECK(!rig.frame.isConfigured());
469 BOOST_CHECK(rig.frame.getGenericTracks().empty());
470 BOOST_CHECK(rig.tracker.getIterationConfigurations().empty());
471
472 configuration.plan.iterations[1].TrackletMinPt = validMinPt;
473 BOOST_REQUIRE(rig.tracker.initialize(rig.frame, configuration));
474 BOOST_REQUIRE(rig.tracker.isConfiguredFor(rig.frame));
475 BOOST_REQUIRE_EQUAL(rig.tracker.getIterationConfigurations().size(), 2u);
476 const auto* iterations = rig.tracker.getIterationConfigurations().data();
477 const auto* catalog = rig.frame.getDetectorConfiguration().getSurfaceCatalog().surfaces;
478 BOOST_CHECK(!rig.tracker.initialize(rig.frame, configuration));
479 BOOST_CHECK(rig.tracker.isConfiguredFor(rig.frame));
480 BOOST_CHECK(rig.tracker.getIterationConfigurations().data() == iterations);
481 BOOST_CHECK(rig.frame.getDetectorConfiguration().getSurfaceCatalog().surfaces == catalog);
482 }
483}
484
485// --- Valid empty input -----------------------------------------------------
486
487BOOST_AUTO_TEST_CASE(ValidEmptyInputCompletesWithoutErrorAndProducesNoTracks)
488{
489 ensureTrivialMagneticFieldIsSet();
490 Rig rig{/*dropTFUponFailure=*/false};
491 rig.establishValidLayout();
492 rig.loadSource(emptyFixture());
493 BOOST_REQUIRE_EQUAL(rig.frame.getTotalMeasurements(), 0u);
494
495 bool result = false;
496 BOOST_CHECK_NO_THROW(result = rig.tracker.run(rig.frame, rig.traits));
497
499 BOOST_CHECK(rig.tracker.getRunStatistics().elapsedMs > 0.f);
500 BOOST_CHECK_EQUAL(rig.frame.getGenericTracks().size(), 0u);
501}
502
503BOOST_AUTO_TEST_CASE(FailedRunClearsPreviousRunStatistics)
504{
505 ensureTrivialMagneticFieldIsSet();
506 for (bool drop : {false, true}) {
507 Rig rig{drop};
508 rig.establishValidLayout();
509 rig.loadSource(emptyFixture());
510 BOOST_REQUIRE(rig.tracker.run(rig.frame, rig.traits));
511 BOOST_REQUIRE(rig.tracker.getRunStatistics().elapsedMs > 0.f);
512 BOOST_REQUIRE_EQUAL(rig.tracker.getRunStatistics().acceptedTrackCounts.size(), 1u);
513
514 rig.frame.resetTimeFrame();
515 rig.loadSource(makeFixture());
516 rig.forceMemoryLimitAtCurrentUsage();
517 if (drop) {
518 BOOST_CHECK(!rig.tracker.run(rig.frame, rig.traits));
519 } else {
520 BOOST_CHECK_THROW(rig.tracker.run(rig.frame, rig.traits), BoundedMemoryResource::MemoryLimitExceeded);
521 }
522 BOOST_CHECK_EQUAL(rig.tracker.getRunStatistics().elapsedMs, 0.f);
523 BOOST_CHECK(rig.tracker.getRunStatistics().acceptedTrackCounts.empty());
524 }
525}
526
527// --- No stale TimeFrame/GenericTrack state survives -------------------------
528//
529// A recoverable-dropped return must clear GenericTrack storage along with
530// the normalized measurements.
531
532BOOST_AUTO_TEST_CASE(RecoverableDroppedLeavesNoStaleGenericTrackState)
533{
534 Rig rig{/*dropTFUponFailure=*/true};
535 rig.establishValidLayout();
536 rig.loadSource(makeFixture());
537 rig.stageStaleState();
538
539 rig.forceMemoryLimitAtCurrentUsage();
540 const auto result = rig.tracker.run(rig.frame, rig.traits);
541
543 BOOST_CHECK(rig.frame.getGenericTracks().empty());
544 BOOST_CHECK(rig.frame.getTrackClusterIndices().empty());
545}
546
547// --- Continued processing after a drop ------------------------------------
548
549BOOST_AUTO_TEST_CASE(TrackerRemainsUsableAfterADroppedTimeFrame)
550{
551 ensureTrivialMagneticFieldIsSet();
552 Rig rig{/*dropTFUponFailure=*/true};
553 rig.establishValidLayout();
554 rig.loadSource(makeFixture());
555
556 rig.forceMemoryLimitAtCurrentUsage();
557 const auto dropped = rig.tracker.run(rig.frame, rig.traits);
558 BOOST_REQUIRE(!dropped);
559
560 // Restore headroom and process a fresh (here, empty) TimeFrame on the
561 // SAME Tracker/TrackerTraits instance -- proving the tracker/device stays
562 // usable after a drop, matching the DPL device staying alive.
563 rig.restoreUnboundedMemory();
564 rig.loadSource(emptyFixture());
565
566 bool result = false;
567 BOOST_CHECK_NO_THROW(result = rig.tracker.run(rig.frame, rig.traits));
569 BOOST_CHECK(rig.tracker.getRunStatistics().elapsedMs > 0.f);
570}
header::DataOrigin origin
Definition of the ITSMFT compact cluster.
Definition of the ClusterTopology class.
Shared CA tracking configuration for ITS and MFT.
Passive common TimeFrame owner.
int32_t i
bool done
Definition of the ITSMFT ROFrame (trigger) record.
Tracker orchestrator.
std::vector< o2::MCCompLabel > labels
Definition of a container to keep Monte Carlo truth external to simulation objects.
Definition of the MagF class.
Runtime-plan-owned, detector-neutral CA workspace.
A container to hold and manage MC truth information/labels.
int ID
Detector identifiers: continuous, starting from 0.
Definition DetID.h:63
UShort_t getSensorID() const
UShort_t getRow() const
Definition CompCluster.h:57
UShort_t getCol() const
Definition CompCluster.h:58
static constexpr unsigned short InvalidPatternID
Definition CompCluster.h:46
bool initialize(TimeFrame &frame, const TrackerInitialization &configuration)
Definition Tracker.cxx:414
Shared CA tracker traits: same ITS-style tracklet/cell/road logic; MFT uses x-y LUT and forward refit...
GLuint64EXT * result
Definition glcorearb.h:5662
GLdouble f
Definition glcorearb.h:310
GLenum const GLfloat * params
Definition glcorearb.h:272
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLint GLuint mask
Definition glcorearb.h:291
constexpr float Radl
Definition Constants.h:34
constexpr float Rho
Definition Constants.h:35
ClusterData< T > extractClusterData(const CompClusterExt &c, gsl::span< const unsigned char >::iterator &patterns, const TopologyDictionary *dict)
Definition IOUtils.h:81
TrackingPlan makeTrackingPlan(const TrackingParameters &parameters)
constexpr std::array< SurfaceDescriptor, ITSNLayers > kITSSurfaces
constexpr int ITSNLayers
ITS CA layer count.
BOOST_AUTO_TEST_CASE(Cluster_messageable)
std::vector< IterationParameters > iterations
const DetectorConfiguration & getDetectorConfiguration() const noexcept
Definition TimeFrame.h:157
void setROFViews(RuntimeROFViews views) noexcept
std::shared_ptr< BoundedMemoryResource > memoryPool
Definition Tracker.h:54
BOOST_CHECK_NO_THROW(algorithm::merge(target, other))
BOOST_CHECK(tree)
coder decode(ctfImage, triggersD, clustersD)
std::vector< Cluster > clusters
BOOST_CHECK_EQUAL(triggersD.size(), triggers.size())