/** * @file ConfigHrtFreqGTest.cpp * @brief Tests UDPSConfigHrtFrequency, which picks the tick rate used to turn * a producer's DATA timestamps into seconds. * * DATA packets carry the raw value of the producer's high-resolution counter. * Until the rate was published in CONFIG the hub divided by its own timer's * frequency, which is only right while the producer runs on the same host — * on x86 that number is the TSC frequency and differs from model to model, so * off-box every accumulated batch was laid out over the wrong span of time. * * The field is a trailer, so these tests pin both directions: a payload that * carries it must be believed, and one that stops early — an older producer — * must still decode against the local fallback rather than against zero. * * @copyright Copyright 2015 F4E | European Joint Undertaking for ITER and * the Development of Fusion Energy ('Fusion for Energy'). * Licensed under the EUPL, Version 1.1 or - as soon they will be approved * by the European Commission - subsequent versions of the EUPL (the "Licence") * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl * * @warning Unless required by applicable law or agreed to in writing, * software distributed under the Licence is distributed on an "AS IS" * basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence permissions and limitations under the Licence. */ #include #include #include "UDPSourceSession.h" using MARTe::uint8; using MARTe::uint32; using MARTe::uint64; using MARTe::float64; using MARTe::UDPS_SIGNAL_DESC_SIZE; using StreamHub::UDPSConfigHrtFrequency; namespace { /** This hub's own timer rate, i.e. what the code must fall back to. */ const float64 kLocalFreq = 1.0e9; /** A plausible producer rate that is deliberately not kLocalFreq. */ const uint64 kWireFreq = 2400000000ULL; const uint32 kNumSigs = 2u; /** Offset of the CONFIG trailer that follows the publish-mode byte. */ uint32 TrailerOffset(uint32 numSigs) { return 4u + (numSigs * UDPS_SIGNAL_DESC_SIZE) + 1u; } /** * Builds a CONFIG payload for kNumSigs signals. * @param withFreq Append the 8-byte HRT frequency trailer. * @param freq Value to append when @p withFreq. * @param[out] size Bytes written. */ const uint8 *BuildConfig(bool withFreq, uint64 freq, uint32 &size) { static uint8 buf[4u + (kNumSigs * UDPS_SIGNAL_DESC_SIZE) + 1u + 8u]; (void) memset(buf, 0, sizeof(buf)); (void) memcpy(buf, &kNumSigs, 4u); size = TrailerOffset(kNumSigs); if (withFreq) { (void) memcpy(buf + size, &freq, 8u); size += 8u; } return buf; } } // namespace /* The whole point of the field: a producer that publishes its rate is believed * even when the hub's own timer runs at a different one. */ TEST(ConfigHrtFreqGTest, AdoptsThePublishedRate) { uint32 size = 0u; const uint8 *cfg = BuildConfig(true, kWireFreq, size); EXPECT_DOUBLE_EQ(static_cast(kWireFreq), UDPSConfigHrtFrequency(cfg, size, kNumSigs, kLocalFreq)); } /* A producer older than the field stops after the publish-mode byte. Reading * past it would take whatever follows in the receive buffer as a frequency. */ TEST(ConfigHrtFreqGTest, FallsBackWhenTheTrailerIsAbsent) { uint32 size = 0u; const uint8 *cfg = BuildConfig(false, 0u, size); EXPECT_DOUBLE_EQ(kLocalFreq, UDPSConfigHrtFrequency(cfg, size, kNumSigs, kLocalFreq)); } /* A trailer cut short mid-field is not a frequency either; taking the bytes * that are there would assemble one out of whatever the rest of the buffer * holds. */ TEST(ConfigHrtFreqGTest, FallsBackOnATruncatedTrailer) { uint32 size = 0u; const uint8 *cfg = BuildConfig(true, kWireFreq, size); EXPECT_DOUBLE_EQ(kLocalFreq, UDPSConfigHrtFrequency(cfg, size - 1u, kNumSigs, kLocalFreq)); } /* Zero is the protocol's "I do not know my own rate". Dividing by it yields * infinities that propagate into every timestamp. */ TEST(ConfigHrtFreqGTest, FallsBackOnTheUnknownSentinel) { uint32 size = 0u; const uint8 *cfg = BuildConfig(true, MARTe::UDPS_HRT_FREQUENCY_UNKNOWN, size); EXPECT_DOUBLE_EQ(kLocalFreq, UDPSConfigHrtFrequency(cfg, size, kNumSigs, kLocalFreq)); } /* No high-resolution timer ticks slower than 1 kHz, so a value that low means * the payload was misread. Adopting it would stretch a millisecond batch * across whole seconds. */ TEST(ConfigHrtFreqGTest, FallsBackOnAnImplausiblyLowRate) { uint32 size = 0u; const uint8 *cfg = BuildConfig(true, 999u, size); EXPECT_DOUBLE_EQ(kLocalFreq, UDPSConfigHrtFrequency(cfg, size, kNumSigs, kLocalFreq)); } /* The trailer sits after the descriptors, so its offset moves with the signal * count; a fixed offset would read descriptor bytes on any other config. */ TEST(ConfigHrtFreqGTest, LocatesTheTrailerAfterTheDescriptors) { const uint32 numSigs = 7u; const uint32 size = TrailerOffset(numSigs) + 8u; uint8 buf[4u + (7u * UDPS_SIGNAL_DESC_SIZE) + 1u + 8u]; /* Fill the descriptor area with a byte pattern that would decode as a * plausible frequency if the offset were wrong. */ (void) memset(buf, 0x11, sizeof(buf)); (void) memcpy(buf, &numSigs, 4u); (void) memcpy(buf + TrailerOffset(numSigs), &kWireFreq, 8u); EXPECT_DOUBLE_EQ(static_cast(kWireFreq), UDPSConfigHrtFrequency(buf, size, numSigs, kLocalFreq)); } /* A null payload must not be dereferenced: CONFIG arrives from the network. */ TEST(ConfigHrtFreqGTest, FallsBackOnANullPayload) { EXPECT_DOUBLE_EQ(kLocalFreq, UDPSConfigHrtFrequency(NULL_PTR(const uint8 *), 1024u, kNumSigs, kLocalFreq)); }