#include #include #include "MinMax.hh" #include "Transition.hh" #include "StringUtil.hh" // SDF module smoke tests - verifying types used by SDF reader/writer namespace sta { class SdfSmokeTest : public ::testing::Test {}; // SDF uses RiseFall for transitions TEST_F(SdfSmokeTest, SdfTripleIndices) { // SDF triples are indexed by rise/fall EXPECT_EQ(RiseFall::riseIndex(), 0); EXPECT_EQ(RiseFall::fallIndex(), 1); } // SDF uses MinMax for min/typ/max TEST_F(SdfSmokeTest, MinMaxForSdf) { EXPECT_NE(MinMax::min(), nullptr); EXPECT_NE(MinMax::max(), nullptr); } // SDF transitions TEST_F(SdfSmokeTest, SdfTransitions) { // SDF has 12 transition types + rise_fall const Transition *t01 = Transition::rise(); const Transition *t10 = Transition::fall(); const Transition *t0z = Transition::tr0Z(); const Transition *tz1 = Transition::trZ1(); EXPECT_NE(t01, nullptr); EXPECT_NE(t10, nullptr); EXPECT_NE(t0z, nullptr); EXPECT_NE(tz1, nullptr); } // Test string comparison used in SDF parsing TEST_F(SdfSmokeTest, StringComparison) { EXPECT_TRUE(stringEq("IOPATH", "IOPATH")); EXPECT_FALSE(stringEq("IOPATH", "iopath")); EXPECT_TRUE(stringEqual("IOPATH", "iopath")); // case insensitive } //////////////////////////////////////////////////////////////// // Additional SDF-relevant type tests //////////////////////////////////////////////////////////////// // Test all 12 SDF transitions TEST_F(SdfSmokeTest, AllSdfTransitions) { // 01 (rise) EXPECT_NE(Transition::rise(), nullptr); EXPECT_NE(Transition::rise()->asRiseFall(), nullptr); EXPECT_EQ(Transition::rise()->asRiseFall(), RiseFall::rise()); // 10 (fall) EXPECT_NE(Transition::fall(), nullptr); EXPECT_NE(Transition::fall()->asRiseFall(), nullptr); EXPECT_EQ(Transition::fall()->asRiseFall(), RiseFall::fall()); // 0Z EXPECT_NE(Transition::tr0Z(), nullptr); EXPECT_NE(Transition::tr0Z()->asInitFinalString(), nullptr); // Z1 EXPECT_NE(Transition::trZ1(), nullptr); // 1Z EXPECT_NE(Transition::tr1Z(), nullptr); // Z0 EXPECT_NE(Transition::trZ0(), nullptr); // 0X EXPECT_NE(Transition::tr0X(), nullptr); // X1 EXPECT_NE(Transition::trX1(), nullptr); // 1X EXPECT_NE(Transition::tr1X(), nullptr); // X0 EXPECT_NE(Transition::trX0(), nullptr); // XZ EXPECT_NE(Transition::trXZ(), nullptr); // ZX EXPECT_NE(Transition::trZX(), nullptr); } // Test transition index values TEST_F(SdfSmokeTest, TransitionIndices) { EXPECT_EQ(Transition::rise()->sdfTripleIndex(), RiseFall::riseIndex()); EXPECT_EQ(Transition::fall()->sdfTripleIndex(), RiseFall::fallIndex()); EXPECT_GE(Transition::maxIndex(), 0); } // Test transition name strings TEST_F(SdfSmokeTest, TransitionNames) { EXPECT_EQ(Transition::rise()->to_string(), "^"); EXPECT_EQ(Transition::fall()->to_string(), "v"); EXPECT_FALSE(Transition::tr0Z()->to_string().empty()); EXPECT_FALSE(Transition::trZ1()->to_string().empty()); } // Test transition find TEST_F(SdfSmokeTest, TransitionFind) { const Transition *rise = Transition::find("^"); EXPECT_EQ(rise, Transition::rise()); const Transition *fall = Transition::find("v"); EXPECT_EQ(fall, Transition::fall()); } // Test transition matches TEST_F(SdfSmokeTest, TransitionMatches) { EXPECT_TRUE(Transition::rise()->matches(Transition::rise())); EXPECT_FALSE(Transition::rise()->matches(Transition::fall())); // riseFall matches both EXPECT_TRUE(Transition::riseFall()->matches(Transition::rise())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::fall())); } // Test RiseFall find TEST_F(SdfSmokeTest, RiseFallFind) { const RiseFall *rise = RiseFall::find("rise"); EXPECT_EQ(rise, RiseFall::rise()); const RiseFall *fall = RiseFall::find("fall"); EXPECT_EQ(fall, RiseFall::fall()); } // Test RiseFall names TEST_F(SdfSmokeTest, RiseFallNames) { EXPECT_STREQ(RiseFall::rise()->name(), "rise"); EXPECT_STREQ(RiseFall::fall()->name(), "fall"); EXPECT_STREQ(RiseFall::rise()->shortName(), "^"); EXPECT_STREQ(RiseFall::fall()->shortName(), "v"); } // Test RiseFall opposite TEST_F(SdfSmokeTest, RiseFallOpposite) { EXPECT_EQ(RiseFall::rise()->opposite(), RiseFall::fall()); EXPECT_EQ(RiseFall::fall()->opposite(), RiseFall::rise()); } // Test RiseFall asRiseFallBoth TEST_F(SdfSmokeTest, RiseFallAsRiseFallBoth) { const RiseFallBoth *rfb = RiseFall::rise()->asRiseFallBoth(); EXPECT_NE(rfb, nullptr); EXPECT_EQ(rfb->asRiseFall(), RiseFall::rise()); } // Test RiseFallBoth TEST_F(SdfSmokeTest, RiseFallBothBasic) { EXPECT_NE(RiseFallBoth::rise(), nullptr); EXPECT_NE(RiseFallBoth::fall(), nullptr); EXPECT_NE(RiseFallBoth::riseFall(), nullptr); EXPECT_STREQ(RiseFallBoth::rise()->name(), "rise"); EXPECT_STREQ(RiseFallBoth::fall()->name(), "fall"); } // Test RiseFallBoth matches TEST_F(SdfSmokeTest, RiseFallBothMatches) { EXPECT_TRUE(RiseFallBoth::rise()->matches(RiseFall::rise())); EXPECT_FALSE(RiseFallBoth::rise()->matches(RiseFall::fall())); EXPECT_TRUE(RiseFallBoth::riseFall()->matches(RiseFall::rise())); EXPECT_TRUE(RiseFallBoth::riseFall()->matches(RiseFall::fall())); } // Test MinMax details TEST_F(SdfSmokeTest, MinMaxDetails) { EXPECT_STREQ(MinMax::min()->to_string().c_str(), "min"); EXPECT_STREQ(MinMax::max()->to_string().c_str(), "max"); EXPECT_EQ(MinMax::min()->index(), MinMax::minIndex()); EXPECT_EQ(MinMax::max()->index(), MinMax::maxIndex()); } // Test MinMax opposite TEST_F(SdfSmokeTest, MinMaxOpposite) { EXPECT_EQ(MinMax::min()->opposite(), MinMax::max()); EXPECT_EQ(MinMax::max()->opposite(), MinMax::min()); } // Test MinMax compare TEST_F(SdfSmokeTest, MinMaxCompare) { // min->compare returns true when value1 < value2 EXPECT_TRUE(MinMax::min()->compare(1.0f, 2.0f)); EXPECT_FALSE(MinMax::min()->compare(2.0f, 1.0f)); // max->compare returns true when value1 > value2 EXPECT_TRUE(MinMax::max()->compare(2.0f, 1.0f)); EXPECT_FALSE(MinMax::max()->compare(1.0f, 2.0f)); } // Test MinMax minMax function TEST_F(SdfSmokeTest, MinMaxMinMaxFunc) { EXPECT_FLOAT_EQ(MinMax::min()->minMax(1.0f, 2.0f), 1.0f); EXPECT_FLOAT_EQ(MinMax::max()->minMax(1.0f, 2.0f), 2.0f); } // Test MinMax find TEST_F(SdfSmokeTest, MinMaxFind) { EXPECT_EQ(MinMax::find("min"), MinMax::min()); EXPECT_EQ(MinMax::find("max"), MinMax::max()); EXPECT_EQ(MinMax::find(0), MinMax::min()); EXPECT_EQ(MinMax::find(1), MinMax::max()); } // Test MinMaxAll TEST_F(SdfSmokeTest, MinMaxAllBasic) { EXPECT_NE(MinMaxAll::min(), nullptr); EXPECT_NE(MinMaxAll::max(), nullptr); EXPECT_NE(MinMaxAll::all(), nullptr); } // Test MinMaxAll matches TEST_F(SdfSmokeTest, MinMaxAllMatches) { EXPECT_TRUE(MinMaxAll::all()->matches(MinMax::min())); EXPECT_TRUE(MinMaxAll::all()->matches(MinMax::max())); EXPECT_TRUE(MinMaxAll::min()->matches(MinMax::min())); EXPECT_FALSE(MinMaxAll::min()->matches(MinMax::max())); } // Test MinMaxAll range TEST_F(SdfSmokeTest, MinMaxAllRange) { auto range = MinMaxAll::all()->range(); EXPECT_EQ(range.size(), 2u); auto min_range = MinMaxAll::min()->range(); EXPECT_EQ(min_range.size(), 1u); } // Test MinMax initValue TEST_F(SdfSmokeTest, MinMaxInitValue) { // min's init value is very large (positive INF) EXPECT_GT(MinMax::min()->initValue(), 0.0f); // max's init value is very negative (-INF) EXPECT_LT(MinMax::max()->initValue(), 0.0f); } //////////////////////////////////////////////////////////////// // Additional SDF-relevant tests for function coverage //////////////////////////////////////////////////////////////// // Test all RiseFall methods TEST_F(SdfSmokeTest, RiseFallIndex) { EXPECT_EQ(RiseFall::rise()->index(), 0); EXPECT_EQ(RiseFall::fall()->index(), 1); } // Test RiseFall::asTransition (Transition.cc coverage) TEST_F(SdfSmokeTest, RiseFallAsTransition) { const Transition *tr_rise = RiseFall::rise()->asTransition(); EXPECT_EQ(tr_rise, Transition::rise()); const Transition *tr_fall = RiseFall::fall()->asTransition(); EXPECT_EQ(tr_fall, Transition::fall()); } // Test RiseFallBoth::find TEST_F(SdfSmokeTest, RiseFallBothFindSdf) { const RiseFallBoth *r = RiseFallBoth::find("rise"); EXPECT_NE(r, nullptr); EXPECT_EQ(r, RiseFallBoth::rise()); const RiseFallBoth *f = RiseFallBoth::find("fall"); EXPECT_NE(f, nullptr); EXPECT_EQ(f, RiseFallBoth::fall()); const RiseFallBoth *rf = RiseFallBoth::find("rise_fall"); EXPECT_NE(rf, nullptr); EXPECT_EQ(rf, RiseFallBoth::riseFall()); } // Test RiseFallBoth::matches(const Transition*) TEST_F(SdfSmokeTest, RiseFallBothMatchesTransition) { EXPECT_TRUE(RiseFallBoth::rise()->matches(Transition::rise())); EXPECT_FALSE(RiseFallBoth::rise()->matches(Transition::fall())); EXPECT_TRUE(RiseFallBoth::fall()->matches(Transition::fall())); EXPECT_FALSE(RiseFallBoth::fall()->matches(Transition::rise())); EXPECT_TRUE(RiseFallBoth::riseFall()->matches(Transition::rise())); EXPECT_TRUE(RiseFallBoth::riseFall()->matches(Transition::fall())); } // Test Transition::asRiseFallBoth TEST_F(SdfSmokeTest, TransitionAsRiseFallBothSdf) { const RiseFallBoth *rfb = Transition::rise()->asRiseFallBoth(); EXPECT_NE(rfb, nullptr); const RiseFallBoth *rfb2 = Transition::fall()->asRiseFallBoth(); EXPECT_NE(rfb2, nullptr); } // Test Transition find with init/final strings (used by SDF reader) TEST_F(SdfSmokeTest, TransitionFindInitFinalSdf) { EXPECT_EQ(Transition::find("01"), Transition::rise()); EXPECT_EQ(Transition::find("10"), Transition::fall()); EXPECT_EQ(Transition::find("0Z"), Transition::tr0Z()); EXPECT_EQ(Transition::find("Z1"), Transition::trZ1()); EXPECT_EQ(Transition::find("1Z"), Transition::tr1Z()); EXPECT_EQ(Transition::find("Z0"), Transition::trZ0()); EXPECT_EQ(Transition::find("0X"), Transition::tr0X()); EXPECT_EQ(Transition::find("X1"), Transition::trX1()); EXPECT_EQ(Transition::find("1X"), Transition::tr1X()); EXPECT_EQ(Transition::find("X0"), Transition::trX0()); EXPECT_EQ(Transition::find("XZ"), Transition::trXZ()); EXPECT_EQ(Transition::find("ZX"), Transition::trZX()); } // Test all transition sdfTripleIndex values TEST_F(SdfSmokeTest, AllTransitionSdfTripleIndex) { EXPECT_EQ(Transition::rise()->sdfTripleIndex(), 0); EXPECT_EQ(Transition::fall()->sdfTripleIndex(), 1); EXPECT_EQ(Transition::tr0Z()->sdfTripleIndex(), 2); EXPECT_EQ(Transition::trZ1()->sdfTripleIndex(), 3); EXPECT_EQ(Transition::tr1Z()->sdfTripleIndex(), 4); EXPECT_EQ(Transition::trZ0()->sdfTripleIndex(), 5); EXPECT_EQ(Transition::tr0X()->sdfTripleIndex(), 6); EXPECT_EQ(Transition::trX1()->sdfTripleIndex(), 7); EXPECT_EQ(Transition::tr1X()->sdfTripleIndex(), 8); EXPECT_EQ(Transition::trX0()->sdfTripleIndex(), 9); EXPECT_EQ(Transition::trXZ()->sdfTripleIndex(), 10); EXPECT_EQ(Transition::trZX()->sdfTripleIndex(), 11); } // Test all transition initFinal strings TEST_F(SdfSmokeTest, AllTransitionInitFinalString) { EXPECT_STREQ(Transition::rise()->asInitFinalString(), "01"); EXPECT_STREQ(Transition::fall()->asInitFinalString(), "10"); EXPECT_STREQ(Transition::tr0Z()->asInitFinalString(), "0Z"); EXPECT_STREQ(Transition::trZ1()->asInitFinalString(), "Z1"); EXPECT_STREQ(Transition::tr1Z()->asInitFinalString(), "1Z"); EXPECT_STREQ(Transition::trZ0()->asInitFinalString(), "Z0"); EXPECT_STREQ(Transition::tr0X()->asInitFinalString(), "0X"); EXPECT_STREQ(Transition::trX1()->asInitFinalString(), "X1"); EXPECT_STREQ(Transition::tr1X()->asInitFinalString(), "1X"); EXPECT_STREQ(Transition::trX0()->asInitFinalString(), "X0"); EXPECT_STREQ(Transition::trXZ()->asInitFinalString(), "XZ"); EXPECT_STREQ(Transition::trZX()->asInitFinalString(), "ZX"); } // Test all transition asRiseFall TEST_F(SdfSmokeTest, AllTransitionAsRiseFall) { EXPECT_EQ(Transition::tr0Z()->asRiseFall(), RiseFall::rise()); EXPECT_EQ(Transition::trZ1()->asRiseFall(), RiseFall::rise()); EXPECT_EQ(Transition::tr0X()->asRiseFall(), RiseFall::rise()); EXPECT_EQ(Transition::trX1()->asRiseFall(), RiseFall::rise()); EXPECT_EQ(Transition::tr1Z()->asRiseFall(), RiseFall::fall()); EXPECT_EQ(Transition::trZ0()->asRiseFall(), RiseFall::fall()); EXPECT_EQ(Transition::tr1X()->asRiseFall(), RiseFall::fall()); EXPECT_EQ(Transition::trX0()->asRiseFall(), RiseFall::fall()); EXPECT_EQ(Transition::trXZ()->asRiseFall(), nullptr); EXPECT_EQ(Transition::trZX()->asRiseFall(), nullptr); } // Test all transition to_string TEST_F(SdfSmokeTest, AllTransitionToString) { EXPECT_FALSE(Transition::tr0Z()->to_string().empty()); EXPECT_FALSE(Transition::trZ1()->to_string().empty()); EXPECT_FALSE(Transition::tr1Z()->to_string().empty()); EXPECT_FALSE(Transition::trZ0()->to_string().empty()); EXPECT_FALSE(Transition::tr0X()->to_string().empty()); EXPECT_FALSE(Transition::trX1()->to_string().empty()); EXPECT_FALSE(Transition::tr1X()->to_string().empty()); EXPECT_FALSE(Transition::trX0()->to_string().empty()); EXPECT_FALSE(Transition::trXZ()->to_string().empty()); EXPECT_FALSE(Transition::trZX()->to_string().empty()); EXPECT_FALSE(Transition::riseFall()->to_string().empty()); } // Test RiseFallBoth range and rangeIndex TEST_F(SdfSmokeTest, RiseFallBothRangesSdf) { auto &rr = RiseFallBoth::rise()->range(); EXPECT_EQ(rr.size(), 1u); auto &fr = RiseFallBoth::fall()->range(); EXPECT_EQ(fr.size(), 1u); auto &rfr = RiseFallBoth::riseFall()->range(); EXPECT_EQ(rfr.size(), 2u); auto &ri = RiseFallBoth::rise()->rangeIndex(); EXPECT_EQ(ri.size(), 1u); auto &fi = RiseFallBoth::fall()->rangeIndex(); EXPECT_EQ(fi.size(), 1u); auto &rfi = RiseFallBoth::riseFall()->rangeIndex(); EXPECT_EQ(rfi.size(), 2u); } // Test MinMax range and rangeIndex TEST_F(SdfSmokeTest, MinMaxRange) { auto &range = MinMax::range(); EXPECT_EQ(range.size(), 2u); auto &ridx = MinMax::rangeIndex(); EXPECT_EQ(ridx.size(), 2u); } // Test Transition matches self TEST_F(SdfSmokeTest, TransitionMatchesSelf) { EXPECT_TRUE(Transition::rise()->matches(Transition::rise())); EXPECT_FALSE(Transition::rise()->matches(Transition::fall())); EXPECT_TRUE(Transition::fall()->matches(Transition::fall())); EXPECT_TRUE(Transition::tr0Z()->matches(Transition::tr0Z())); EXPECT_FALSE(Transition::tr0Z()->matches(Transition::trZ1())); } // Test MinMaxAll asMinMax TEST_F(SdfSmokeTest, MinMaxAllAsMinMax) { EXPECT_EQ(MinMaxAll::min()->asMinMax(), MinMax::min()); EXPECT_EQ(MinMaxAll::max()->asMinMax(), MinMax::max()); } // SdfTriple tests (class defined in SdfReader.cc, not directly accessible // but we test the concepts that SDF triple values represent) // Test SDF edge names for transitions // Covers: SdfWriter::sdfEdge - exercises transition to SDF edge string mapping TEST_F(SdfSmokeTest, TransitionAsInitFinalString) { // SDF uses init/final transition string representation EXPECT_NE(Transition::rise()->asInitFinalString(), nullptr); EXPECT_NE(Transition::fall()->asInitFinalString(), nullptr); EXPECT_NE(Transition::tr0Z()->asInitFinalString(), nullptr); EXPECT_NE(Transition::trZ1()->asInitFinalString(), nullptr); EXPECT_NE(Transition::tr1Z()->asInitFinalString(), nullptr); EXPECT_NE(Transition::trZ0()->asInitFinalString(), nullptr); EXPECT_NE(Transition::tr0X()->asInitFinalString(), nullptr); EXPECT_NE(Transition::trX1()->asInitFinalString(), nullptr); EXPECT_NE(Transition::tr1X()->asInitFinalString(), nullptr); EXPECT_NE(Transition::trX0()->asInitFinalString(), nullptr); } // Test Transition asRiseFall for SDF edge transitions TEST_F(SdfSmokeTest, TransitionAsRiseFallAll) { // Rise transitions map to rise EXPECT_EQ(Transition::rise()->asRiseFall(), RiseFall::rise()); // Fall transitions map to fall EXPECT_EQ(Transition::fall()->asRiseFall(), RiseFall::fall()); // Z1 maps to rise EXPECT_EQ(Transition::trZ1()->asRiseFall(), RiseFall::rise()); // Z0 maps to fall EXPECT_EQ(Transition::trZ0()->asRiseFall(), RiseFall::fall()); // 0Z maps to rise EXPECT_EQ(Transition::tr0Z()->asRiseFall(), RiseFall::rise()); // 1Z maps to fall EXPECT_EQ(Transition::tr1Z()->asRiseFall(), RiseFall::fall()); } // Test MinMaxAll matches method TEST_F(SdfSmokeTest, MinMaxAllMatches2) { EXPECT_TRUE(MinMaxAll::min()->matches(MinMax::min())); EXPECT_FALSE(MinMaxAll::min()->matches(MinMax::max())); EXPECT_TRUE(MinMaxAll::max()->matches(MinMax::max())); EXPECT_FALSE(MinMaxAll::max()->matches(MinMax::min())); EXPECT_TRUE(MinMaxAll::all()->matches(MinMax::min())); EXPECT_TRUE(MinMaxAll::all()->matches(MinMax::max())); } // Test MinMaxAll matches MinMaxAll TEST_F(SdfSmokeTest, MinMaxAllMatchesAll) { EXPECT_TRUE(MinMaxAll::all()->matches(MinMaxAll::min())); EXPECT_TRUE(MinMaxAll::all()->matches(MinMaxAll::max())); EXPECT_TRUE(MinMaxAll::all()->matches(MinMaxAll::all())); EXPECT_TRUE(MinMaxAll::min()->matches(MinMaxAll::min())); EXPECT_FALSE(MinMaxAll::min()->matches(MinMaxAll::max())); } // Test MinMax find by name TEST_F(SdfSmokeTest, MinMaxFindByName) { EXPECT_EQ(MinMax::find("min"), MinMax::min()); EXPECT_EQ(MinMax::find("max"), MinMax::max()); EXPECT_EQ(MinMax::find("nonexistent"), nullptr); } // Test MinMax find by index TEST_F(SdfSmokeTest, MinMaxFindByIndex) { EXPECT_EQ(MinMax::find(MinMax::minIndex()), MinMax::min()); EXPECT_EQ(MinMax::find(MinMax::maxIndex()), MinMax::max()); } // Test MinMaxAll find by name TEST_F(SdfSmokeTest, MinMaxAllFindByName) { EXPECT_EQ(MinMaxAll::find("min"), MinMaxAll::min()); EXPECT_EQ(MinMaxAll::find("max"), MinMaxAll::max()); EXPECT_EQ(MinMaxAll::find("all"), MinMaxAll::all()); EXPECT_EQ(MinMaxAll::find("nonexistent"), nullptr); } // Test MinMax opposite TEST_F(SdfSmokeTest, MinMaxOpposite2) { EXPECT_EQ(MinMax::min()->opposite(), MinMax::max()); EXPECT_EQ(MinMax::max()->opposite(), MinMax::min()); } // Test MinMax minMax function TEST_F(SdfSmokeTest, MinMaxMinMaxFunc2) { EXPECT_FLOAT_EQ(MinMax::min()->minMax(3.0f, 5.0f), 3.0f); EXPECT_FLOAT_EQ(MinMax::min()->minMax(5.0f, 3.0f), 3.0f); EXPECT_FLOAT_EQ(MinMax::max()->minMax(3.0f, 5.0f), 5.0f); EXPECT_FLOAT_EQ(MinMax::max()->minMax(5.0f, 3.0f), 5.0f); } // Test MinMax to_string TEST_F(SdfSmokeTest, MinMaxToString) { EXPECT_EQ(MinMax::min()->to_string(), "min"); EXPECT_EQ(MinMax::max()->to_string(), "max"); } // Test MinMaxAll to_string TEST_F(SdfSmokeTest, MinMaxAllToString) { EXPECT_EQ(MinMaxAll::min()->to_string(), "min"); EXPECT_EQ(MinMaxAll::max()->to_string(), "max"); EXPECT_EQ(MinMaxAll::all()->to_string(), "all"); } // Test MinMax initValueInt TEST_F(SdfSmokeTest, MinMaxInitValueInt) { // min's init value is a large positive integer EXPECT_GT(MinMax::min()->initValueInt(), 0); // max's init value is a large negative integer EXPECT_LT(MinMax::max()->initValueInt(), 0); } // Test MinMaxAll rangeIndex TEST_F(SdfSmokeTest, MinMaxAllRangeIndex) { auto &min_range_idx = MinMaxAll::min()->rangeIndex(); EXPECT_EQ(min_range_idx.size(), 1u); EXPECT_EQ(min_range_idx[0], MinMax::minIndex()); auto &max_range_idx = MinMaxAll::max()->rangeIndex(); EXPECT_EQ(max_range_idx.size(), 1u); EXPECT_EQ(max_range_idx[0], MinMax::maxIndex()); auto &all_range_idx = MinMaxAll::all()->rangeIndex(); EXPECT_EQ(all_range_idx.size(), 2u); } // Test MinMax constructor coverage (exercises internal MinMax ctor) // Covers: MinMax::MinMax(const char*, int, float, int, compare_fn) TEST_F(SdfSmokeTest, MinMaxConstructorCoverage) { // The MinMax constructor is private; we verify it was called via singletons const MinMax *mn = MinMax::min(); EXPECT_STREQ(mn->to_string().c_str(), "min"); EXPECT_EQ(mn->index(), MinMax::minIndex()); EXPECT_GT(mn->initValue(), 0.0f); EXPECT_GT(mn->initValueInt(), 0); // Compare function: min returns true when value1 < value2 EXPECT_TRUE(mn->compare(1.0f, 2.0f)); EXPECT_FALSE(mn->compare(3.0f, 2.0f)); const MinMax *mx = MinMax::max(); EXPECT_STREQ(mx->to_string().c_str(), "max"); EXPECT_EQ(mx->index(), MinMax::maxIndex()); EXPECT_LT(mx->initValue(), 0.0f); EXPECT_LT(mx->initValueInt(), 0); EXPECT_TRUE(mx->compare(3.0f, 2.0f)); EXPECT_FALSE(mx->compare(1.0f, 2.0f)); } // Test MinMax minMax with equal values // Covers: MinMax::minMax edge case TEST_F(SdfSmokeTest, MinMaxMinMaxEqualValues) { EXPECT_FLOAT_EQ(MinMax::min()->minMax(5.0f, 5.0f), 5.0f); EXPECT_FLOAT_EQ(MinMax::max()->minMax(5.0f, 5.0f), 5.0f); } // Test MinMaxAll index values // Covers: MinMaxAll constructor internals TEST_F(SdfSmokeTest, MinMaxAllIndices) { EXPECT_EQ(MinMaxAll::min()->index(), 0); EXPECT_EQ(MinMaxAll::max()->index(), 1); EXPECT_EQ(MinMaxAll::all()->index(), 2); } // Test MinMax find with nullptr // Covers: MinMax::find edge case TEST_F(SdfSmokeTest, MinMaxFindNull) { const MinMax *result = MinMax::find("invalid_string"); EXPECT_EQ(result, nullptr); } // Test RiseFall asRiseFallBoth // Covers: RiseFall::asRiseFallBoth TEST_F(SdfSmokeTest, RiseFallAsRiseFallBoth2) { const RiseFallBoth *rfb = RiseFall::rise()->asRiseFallBoth(); EXPECT_EQ(rfb, RiseFallBoth::rise()); rfb = RiseFall::fall()->asRiseFallBoth(); EXPECT_EQ(rfb, RiseFallBoth::fall()); } // Test Transition::riseFall matches all transitions // Covers: Transition::matches for riseFall wildcard TEST_F(SdfSmokeTest, TransitionRiseFallMatchesAll) { EXPECT_TRUE(Transition::riseFall()->matches(Transition::rise())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::fall())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::tr0Z())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::trZ1())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::tr1Z())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::trZ0())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::tr0X())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::trX1())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::tr1X())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::trX0())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::trXZ())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::trZX())); } // Test Transition::find with empty/unknown string // Covers: Transition::find edge case TEST_F(SdfSmokeTest, TransitionFindUnknown) { const Transition *t = Transition::find("nonexistent"); EXPECT_EQ(t, nullptr); } // Test RiseFall::find with unknown string // Covers: RiseFall::find edge case TEST_F(SdfSmokeTest, RiseFallFindUnknown) { const RiseFall *rf = RiseFall::find("unknown"); EXPECT_EQ(rf, nullptr); } // Test Transition::maxIndex value // Covers: Transition::maxIndex TEST_F(SdfSmokeTest, TransitionMaxIndex) { // Transition::maxIndex() returns the max valid index EXPECT_GE(Transition::maxIndex(), 1); // All transitions should have index <= maxIndex EXPECT_LE(Transition::rise()->index(), Transition::maxIndex()); EXPECT_LE(Transition::fall()->index(), Transition::maxIndex()); } // Test RiseFall to_string // Covers: RiseFall::to_string TEST_F(SdfSmokeTest, RiseFallToString) { EXPECT_EQ(RiseFall::rise()->to_string(), "rise"); EXPECT_EQ(RiseFall::fall()->to_string(), "fall"); } // Test MinMax compare with equal values // Covers: MinMax::compare edge case TEST_F(SdfSmokeTest, MinMaxCompareEqual) { EXPECT_FALSE(MinMax::min()->compare(5.0f, 5.0f)); EXPECT_FALSE(MinMax::max()->compare(5.0f, 5.0f)); } // Test MinMax compare with negative values // Covers: MinMax::compare with negatives TEST_F(SdfSmokeTest, MinMaxCompareNegative) { EXPECT_TRUE(MinMax::min()->compare(-2.0f, -1.0f)); EXPECT_FALSE(MinMax::min()->compare(-1.0f, -2.0f)); EXPECT_TRUE(MinMax::max()->compare(-1.0f, -2.0f)); EXPECT_FALSE(MinMax::max()->compare(-2.0f, -1.0f)); } // Test MinMax compare with zero // Covers: MinMax::compare with zero TEST_F(SdfSmokeTest, MinMaxCompareZero) { EXPECT_TRUE(MinMax::min()->compare(0.0f, 1.0f)); EXPECT_FALSE(MinMax::min()->compare(0.0f, 0.0f)); EXPECT_TRUE(MinMax::max()->compare(1.0f, 0.0f)); EXPECT_FALSE(MinMax::max()->compare(0.0f, 0.0f)); } // Test MinMaxAll range sizes // Covers: MinMaxAll::range TEST_F(SdfSmokeTest, MinMaxAllRangeSizes) { EXPECT_EQ(MinMaxAll::min()->range().size(), 1u); EXPECT_EQ(MinMaxAll::max()->range().size(), 1u); EXPECT_EQ(MinMaxAll::all()->range().size(), 2u); } // Test Transition sdfTripleIndex uniqueness // Covers: Transition::sdfTripleIndex TEST_F(SdfSmokeTest, TransitionSdfTripleIndexUnique) { std::set indices; indices.insert(Transition::rise()->sdfTripleIndex()); indices.insert(Transition::fall()->sdfTripleIndex()); indices.insert(Transition::tr0Z()->sdfTripleIndex()); indices.insert(Transition::trZ1()->sdfTripleIndex()); indices.insert(Transition::tr1Z()->sdfTripleIndex()); indices.insert(Transition::trZ0()->sdfTripleIndex()); indices.insert(Transition::tr0X()->sdfTripleIndex()); indices.insert(Transition::trX1()->sdfTripleIndex()); indices.insert(Transition::tr1X()->sdfTripleIndex()); indices.insert(Transition::trX0()->sdfTripleIndex()); indices.insert(Transition::trXZ()->sdfTripleIndex()); indices.insert(Transition::trZX()->sdfTripleIndex()); EXPECT_EQ(indices.size(), 12u); } // Test RiseFall range iteration // Covers: RiseFall::range TEST_F(SdfSmokeTest, RiseFallRangeIteration) { int count = 0; for (auto rf : RiseFall::range()) { EXPECT_NE(rf, nullptr); count++; } EXPECT_EQ(count, 2); } // Test MinMax range // Covers: MinMax::range TEST_F(SdfSmokeTest, MinMaxRangeIteration) { int count = 0; for (auto mm : MinMax::range()) { EXPECT_NE(mm, nullptr); count++; } EXPECT_EQ(count, 2); } // Test RiseFallBoth find with nullptr // Covers: RiseFallBoth::find edge case TEST_F(SdfSmokeTest, RiseFallBothFindNull) { const RiseFallBoth *result = RiseFallBoth::find("nonexistent"); EXPECT_EQ(result, nullptr); } // Test Transition asRiseFallBoth for non-rise/fall transitions // Covers: Transition::asRiseFallBoth edge cases TEST_F(SdfSmokeTest, TransitionAsRiseFallBothTristate) { // Tristate transitions should still return valid RiseFallBoth const RiseFallBoth *rfb_0z = Transition::tr0Z()->asRiseFallBoth(); EXPECT_NE(rfb_0z, nullptr); } // Test Transition to_string for riseFall wildcard // Covers: Transition::to_string for riseFall TEST_F(SdfSmokeTest, TransitionRiseFallToString) { EXPECT_FALSE(Transition::riseFall()->to_string().empty()); } //////////////////////////////////////////////////////////////// // R8_ tests for SDF module coverage improvement // SdfWriter, SdfTriple, SdfPortSpec are all internal/protected. // We exercise them through the full STA flow. //////////////////////////////////////////////////////////////// } // namespace sta #include #include #include "Sta.hh" #include "Network.hh" #include "ReportTcl.hh" #include "Scene.hh" #include "Error.hh" #include "sdf/SdfReader.hh" namespace sta { class SdfDesignTest : public ::testing::Test { protected: void SetUp() override { interp_ = Tcl_CreateInterp(); initSta(); sta_ = new Sta; Sta::setSta(sta_); sta_->makeComponents(); ReportTcl *report = dynamic_cast(sta_->report()); if (report) report->setTclInterp(interp_); Scene *corner = sta_->cmdScene(); const MinMaxAll *min_max = MinMaxAll::all(); bool infer_latches = false; LibertyLibrary *lib_seq = sta_->readLiberty( "test/asap7/asap7sc7p5t_SEQ_RVT_FF_nldm_220123.lib", corner, min_max, infer_latches); if (!lib_seq) { design_loaded_ = false; return; } LibertyLibrary *lib_inv = sta_->readLiberty( "test/asap7/asap7sc7p5t_INVBUF_RVT_FF_nldm_220122.lib.gz", corner, min_max, infer_latches); if (!lib_inv) { design_loaded_ = false; return; } LibertyLibrary *lib_simple = sta_->readLiberty( "test/asap7/asap7sc7p5t_SIMPLE_RVT_FF_nldm_211120.lib.gz", corner, min_max, infer_latches); if (!lib_simple) { design_loaded_ = false; return; } LibertyLibrary *lib_oa = sta_->readLiberty( "test/asap7/asap7sc7p5t_OA_RVT_FF_nldm_211120.lib.gz", corner, min_max, infer_latches); if (!lib_oa) { design_loaded_ = false; return; } LibertyLibrary *lib_ao = sta_->readLiberty( "test/asap7/asap7sc7p5t_AO_RVT_FF_nldm_211120.lib.gz", corner, min_max, infer_latches); if (!lib_ao) { design_loaded_ = false; return; } bool verilog_ok = sta_->readVerilog("test/reg1_asap7.v"); if (!verilog_ok) { design_loaded_ = false; return; } bool linked = sta_->linkDesign("top", true); if (!linked) { design_loaded_ = false; return; } design_loaded_ = true; } void TearDown() override { deleteAllMemory(); sta_ = nullptr; if (interp_) Tcl_DeleteInterp(interp_); interp_ = nullptr; } Sta *sta_; Tcl_Interp *interp_; bool design_loaded_ = false; }; // Test writeSdf exercises SdfWriter constructor/destructor, // writeTrailer, writeInstTrailer, sdfEdge, writeTimingCheckHeader/Trailer // Covers: SdfWriter::~SdfWriter, SdfWriter::writeTrailer, // SdfWriter::writeInstTrailer, SdfWriter::writeTimingCheckHeader, // SdfWriter::writeTimingCheckTrailer, SdfWriter::sdfEdge TEST_F(SdfDesignTest, WriteSdfExercisesWriter) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); // Read SPEF to have timing data Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); // Write SDF to a temp file - this exercises all SdfWriter methods const char *tmpfile = "/tmp/test_r8_sdf_output.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 3, false, true, true); // Verify the file was created and has content FILE *f = fopen(tmpfile, "r"); ASSERT_NE(f, nullptr); fseek(f, 0, SEEK_END); long size = ftell(f); fclose(f); EXPECT_GT(size, 0); // Clean up std::remove(tmpfile); } // Test writeSdf with gzip // Covers: SdfWriter methods through gzip path TEST_F(SdfDesignTest, WriteSdfGzip) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r8_sdf_output.sdf.gz"; sta_->writeSdf(tmpfile, corner, '/', false, 3, true, true, true); // Verify the file was created FILE *f = fopen(tmpfile, "r"); ASSERT_NE(f, nullptr); fseek(f, 0, SEEK_END); long size = ftell(f); fclose(f); EXPECT_GT(size, 0); std::remove(tmpfile); } //////////////////////////////////////////////////////////////// // R9_ tests for SDF module coverage improvement //////////////////////////////////////////////////////////////// // Test writeSdf with dot divider // Covers: SdfWriter path separator handling TEST_F(SdfDesignTest, WriteSdfDotDivider) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_dot.sdf"; sta_->writeSdf(tmpfile, corner, '.', true, 3, false, true, true); FILE *f = fopen(tmpfile, "r"); ASSERT_NE(f, nullptr); fseek(f, 0, SEEK_END); long size = ftell(f); fclose(f); EXPECT_GT(size, 0); std::remove(tmpfile); } // Test writeSdf without typ values // Covers: SdfWriter min/max only path TEST_F(SdfDesignTest, WriteSdfNoTyp) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_notyp.sdf"; sta_->writeSdf(tmpfile, corner, '/', false, 3, false, true, true); FILE *f = fopen(tmpfile, "r"); ASSERT_NE(f, nullptr); fseek(f, 0, SEEK_END); long size = ftell(f); fclose(f); EXPECT_GT(size, 0); std::remove(tmpfile); } // Test writeSdf with high precision // Covers: SdfWriter digit formatting TEST_F(SdfDesignTest, WriteSdfHighPrecision) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_highprec.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 6, false, true, true); FILE *f = fopen(tmpfile, "r"); ASSERT_NE(f, nullptr); fseek(f, 0, SEEK_END); long size = ftell(f); fclose(f); EXPECT_GT(size, 0); std::remove(tmpfile); } // Test writeSdf with no_timestamp // Covers: SdfWriter header control TEST_F(SdfDesignTest, WriteSdfNoTimestamp) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_notimestamp.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 3, false, false, true); FILE *f = fopen(tmpfile, "r"); ASSERT_NE(f, nullptr); fseek(f, 0, SEEK_END); long size = ftell(f); fclose(f); EXPECT_GT(size, 0); std::remove(tmpfile); } // Test writeSdf with no_timescale // Covers: SdfWriter timescale control TEST_F(SdfDesignTest, WriteSdfNoTimescale) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_notimescale.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 3, false, true, false); FILE *f = fopen(tmpfile, "r"); ASSERT_NE(f, nullptr); fseek(f, 0, SEEK_END); long size = ftell(f); fclose(f); EXPECT_GT(size, 0); std::remove(tmpfile); } // Test writeSdf and readSdf round-trip // Covers: readSdf, SdfReader constructor, SdfScanner, SdfPortSpec, SdfTriple TEST_F(SdfDesignTest, WriteThenReadSdf) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_roundtrip.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 3, false, true, true); // Now read it back readSdf(tmpfile, nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_); std::remove(tmpfile); } // Test readSdf with unescaped_dividers option // Covers: SdfReader unescaped divider path TEST_F(SdfDesignTest, ReadSdfUnescapedDividers) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_unesc.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 3, false, true, true); readSdf(tmpfile, nullptr, corner, true, false, const_cast(MinMaxAll::all()), sta_); std::remove(tmpfile); } // Test readSdf with incremental_only option // Covers: SdfReader incremental_only path TEST_F(SdfDesignTest, ReadSdfIncrementalOnly) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_incr.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 3, false, true, true); readSdf(tmpfile, nullptr, corner, false, true, const_cast(MinMaxAll::all()), sta_); std::remove(tmpfile); } // Test readSdf with cond_use min // Covers: SdfReader cond_use min path TEST_F(SdfDesignTest, ReadSdfCondUseMin) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_cumin.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 3, false, true, true); readSdf(tmpfile, nullptr, corner, false, false, const_cast(MinMaxAll::min()), sta_); std::remove(tmpfile); } // Test readSdf with cond_use max // Covers: SdfReader cond_use max path TEST_F(SdfDesignTest, ReadSdfCondUseMax) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_cumax.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 3, false, true, true); readSdf(tmpfile, nullptr, corner, false, false, const_cast(MinMaxAll::max()), sta_); std::remove(tmpfile); } // Test writeSdf then read with both unescaped and incremental // Covers: combined SdfReader option paths TEST_F(SdfDesignTest, ReadSdfCombinedOptions) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_combined.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 3, false, true, true); readSdf(tmpfile, nullptr, corner, true, true, const_cast(MinMaxAll::all()), sta_); std::remove(tmpfile); } // Test writeSdf with low precision (1 digit) // Covers: SdfWriter digit formatting edge case TEST_F(SdfDesignTest, WriteSdfLowPrecision) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_lowprec.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 1, false, true, true); FILE *f = fopen(tmpfile, "r"); ASSERT_NE(f, nullptr); fseek(f, 0, SEEK_END); long size = ftell(f); fclose(f); EXPECT_GT(size, 0); std::remove(tmpfile); } // Test writeSdf gzip then readSdf // Covers: SdfReader gzip input path TEST_F(SdfDesignTest, WriteSdfGzipThenRead) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_gz.sdf.gz"; sta_->writeSdf(tmpfile, corner, '/', true, 3, true, true, true); readSdf(tmpfile, nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_); std::remove(tmpfile); } // Test writeSdf with no_timestamp and no_timescale // Covers: SdfWriter combined header options TEST_F(SdfDesignTest, WriteSdfNoTimestampNoTimescale) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r9_sdf_minimal.sdf"; sta_->writeSdf(tmpfile, corner, '/', false, 3, false, false, false); FILE *f = fopen(tmpfile, "r"); ASSERT_NE(f, nullptr); fseek(f, 0, SEEK_END); long size = ftell(f); fclose(f); EXPECT_GT(size, 0); std::remove(tmpfile); } // Test readSdf with nonexistent file throws FileNotReadable // Covers: SdfReader error handling path TEST_F(SdfDesignTest, ReadSdfNonexistent) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); // Attempt to read nonexistent SDF - should throw EXPECT_THROW( readSdf("/tmp/nonexistent_r9.sdf", nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_), FileNotReadable ); } // R9_ SdfSmokeTest - Transition properties for SDF TEST_F(SdfSmokeTest, TransitionRiseProperties) { const Transition *t = Transition::rise(); EXPECT_NE(t, nullptr); EXPECT_EQ(t->asRiseFall(), RiseFall::rise()); EXPECT_EQ(t->sdfTripleIndex(), RiseFall::riseIndex()); EXPECT_FALSE(t->to_string().empty()); EXPECT_NE(t->asInitFinalString(), nullptr); } TEST_F(SdfSmokeTest, TransitionFallProperties) { const Transition *t = Transition::fall(); EXPECT_NE(t, nullptr); EXPECT_EQ(t->asRiseFall(), RiseFall::fall()); EXPECT_EQ(t->sdfTripleIndex(), RiseFall::fallIndex()); EXPECT_FALSE(t->to_string().empty()); EXPECT_NE(t->asInitFinalString(), nullptr); } TEST_F(SdfSmokeTest, TransitionTristateProperties) { // 0Z const Transition *t0z = Transition::tr0Z(); EXPECT_NE(t0z, nullptr); EXPECT_FALSE(t0z->to_string().empty()); EXPECT_NE(t0z->asRiseFallBoth(), nullptr); // Z1 const Transition *tz1 = Transition::trZ1(); EXPECT_NE(tz1, nullptr); EXPECT_NE(tz1->asRiseFallBoth(), nullptr); // 1Z const Transition *t1z = Transition::tr1Z(); EXPECT_NE(t1z, nullptr); EXPECT_NE(t1z->asRiseFallBoth(), nullptr); // Z0 const Transition *tz0 = Transition::trZ0(); EXPECT_NE(tz0, nullptr); EXPECT_NE(tz0->asRiseFallBoth(), nullptr); } TEST_F(SdfSmokeTest, TransitionUnknownProperties) { // 0X const Transition *t0x = Transition::tr0X(); EXPECT_NE(t0x, nullptr); EXPECT_NE(t0x->asRiseFallBoth(), nullptr); // X1 const Transition *tx1 = Transition::trX1(); EXPECT_NE(tx1, nullptr); EXPECT_NE(tx1->asRiseFallBoth(), nullptr); // 1X const Transition *t1x = Transition::tr1X(); EXPECT_NE(t1x, nullptr); EXPECT_NE(t1x->asRiseFallBoth(), nullptr); // X0 const Transition *tx0 = Transition::trX0(); EXPECT_NE(tx0, nullptr); EXPECT_NE(tx0->asRiseFallBoth(), nullptr); } TEST_F(SdfSmokeTest, TransitionHighZUnknown) { // XZ const Transition *txz = Transition::trXZ(); EXPECT_NE(txz, nullptr); EXPECT_FALSE(txz->to_string().empty()); // ZX const Transition *tzx = Transition::trZX(); EXPECT_NE(tzx, nullptr); EXPECT_FALSE(tzx->to_string().empty()); } TEST_F(SdfSmokeTest, RiseFallBothRiseFallMatches) { EXPECT_TRUE(RiseFallBoth::riseFall()->matches(RiseFall::rise())); EXPECT_TRUE(RiseFallBoth::riseFall()->matches(RiseFall::fall())); EXPECT_TRUE(RiseFallBoth::rise()->matches(RiseFall::rise())); EXPECT_FALSE(RiseFallBoth::rise()->matches(RiseFall::fall())); EXPECT_FALSE(RiseFallBoth::fall()->matches(RiseFall::rise())); EXPECT_TRUE(RiseFallBoth::fall()->matches(RiseFall::fall())); } TEST_F(SdfSmokeTest, MinMaxAllRange2) { // Verify MinMaxAll::all() range contains both min and max int count = 0; for (auto mm : MinMaxAll::all()->range()) { EXPECT_NE(mm, nullptr); count++; } EXPECT_EQ(count, 2); } TEST_F(SdfSmokeTest, MinMaxInitValue2) { // min init value is large positive (for finding minimum) float min_init = MinMax::min()->initValue(); EXPECT_GT(min_init, 0.0f); // max init value is large negative (for finding maximum) float max_init = MinMax::max()->initValue(); EXPECT_LT(max_init, 0.0f); } TEST_F(SdfSmokeTest, MinMaxCompareExtremes) { // Very large values EXPECT_TRUE(MinMax::min()->compare(1e10f, 1e20f)); EXPECT_FALSE(MinMax::min()->compare(1e20f, 1e10f)); EXPECT_TRUE(MinMax::max()->compare(1e20f, 1e10f)); EXPECT_FALSE(MinMax::max()->compare(1e10f, 1e20f)); // Very small values EXPECT_TRUE(MinMax::min()->compare(1e-20f, 1e-10f)); EXPECT_TRUE(MinMax::max()->compare(1e-10f, 1e-20f)); } TEST_F(SdfSmokeTest, RiseFallToStringAndFind) { EXPECT_EQ(RiseFall::rise()->to_string(), "rise"); EXPECT_EQ(RiseFall::fall()->to_string(), "fall"); EXPECT_EQ(RiseFall::find("^"), RiseFall::rise()); EXPECT_EQ(RiseFall::find("v"), RiseFall::fall()); EXPECT_EQ(RiseFall::find("rise"), RiseFall::rise()); EXPECT_EQ(RiseFall::find("fall"), RiseFall::fall()); } TEST_F(SdfSmokeTest, TransitionFindByName) { EXPECT_EQ(Transition::find("^"), Transition::rise()); EXPECT_EQ(Transition::find("v"), Transition::fall()); EXPECT_EQ(Transition::find("nonexistent"), nullptr); } TEST_F(SdfSmokeTest, MinMaxAllAsMinMax2) { EXPECT_EQ(MinMaxAll::min()->asMinMax(), MinMax::min()); EXPECT_EQ(MinMaxAll::max()->asMinMax(), MinMax::max()); } TEST_F(SdfSmokeTest, RiseFallOpposite2) { EXPECT_EQ(RiseFall::rise()->opposite(), RiseFall::fall()); EXPECT_EQ(RiseFall::fall()->opposite(), RiseFall::rise()); } TEST_F(SdfSmokeTest, TransitionMatchesSelf2) { EXPECT_TRUE(Transition::rise()->matches(Transition::rise())); EXPECT_TRUE(Transition::fall()->matches(Transition::fall())); EXPECT_FALSE(Transition::rise()->matches(Transition::fall())); EXPECT_FALSE(Transition::fall()->matches(Transition::rise())); } TEST_F(SdfSmokeTest, TransitionMatchesRiseFallWildcard) { EXPECT_TRUE(Transition::riseFall()->matches(Transition::rise())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::fall())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::tr0Z())); EXPECT_TRUE(Transition::riseFall()->matches(Transition::trXZ())); } TEST_F(SdfSmokeTest, MinMaxMinMaxFunc3) { EXPECT_FLOAT_EQ(MinMax::min()->minMax(10.0f, 20.0f), 10.0f); EXPECT_FLOAT_EQ(MinMax::max()->minMax(10.0f, 20.0f), 20.0f); EXPECT_FLOAT_EQ(MinMax::min()->minMax(-5.0f, 5.0f), -5.0f); EXPECT_FLOAT_EQ(MinMax::max()->minMax(-5.0f, 5.0f), 5.0f); } TEST_F(SdfSmokeTest, RiseFallBothFind) { EXPECT_EQ(RiseFallBoth::find("rise"), RiseFallBoth::rise()); EXPECT_EQ(RiseFallBoth::find("fall"), RiseFallBoth::fall()); EXPECT_EQ(RiseFallBoth::find("rise_fall"), RiseFallBoth::riseFall()); EXPECT_EQ(RiseFallBoth::find("nonexistent"), nullptr); } // ========================================================================= // R11_ tests: Cover additional uncovered SDF functions // ========================================================================= // R11_1: Write SDF then read it with a path argument // Covers: SdfReader with path set, SdfTriple construction paths TEST_F(SdfDesignTest, ReadSdfWithPath) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r11_sdf_path.sdf"; sta_->writeSdf(tmpfile, corner, '/', true, 3, false, true, true); // Read with a path argument (top instance path) readSdf(tmpfile, "top", corner, false, false, const_cast(MinMaxAll::all()), sta_); std::remove(tmpfile); } // R11_2: Read a hand-crafted SDF with specific constructs to exercise // SdfReader::makeTriple(), makeTriple(float), SdfPortSpec, SdfTriple::hasValue TEST_F(SdfDesignTest, ReadHandCraftedSdf) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); // Write a hand-crafted SDF with IOPATH and timing checks const char *sdf_path = "/tmp/test_r11_handcraft.sdf"; FILE *fp = fopen(sdf_path, "w"); ASSERT_NE(fp, nullptr); fprintf(fp, "(DELAYFILE\n"); fprintf(fp, " (SDFVERSION \"3.0\")\n"); fprintf(fp, " (DESIGN \"top\")\n"); fprintf(fp, " (TIMESCALE 1ns)\n"); fprintf(fp, " (CELL\n"); fprintf(fp, " (CELLTYPE \"DFFHQx4_ASAP7_75t_R\")\n"); fprintf(fp, " (INSTANCE r1)\n"); fprintf(fp, " (DELAY\n"); fprintf(fp, " (ABSOLUTE\n"); fprintf(fp, " (IOPATH CLK Q (0.100::0.200) (0.150::0.250))\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, " (TIMINGCHECK\n"); fprintf(fp, " (SETUP D (posedge CLK) (0.050::0.080))\n"); fprintf(fp, " (HOLD D (posedge CLK) (0.020::0.030))\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, ")\n"); fclose(fp); readSdf(sdf_path, nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_); std::remove(sdf_path); } // R11_3: Read SDF with edge-specific IOPATH (posedge, negedge) // Covers: SdfPortSpec with transitions, sdfEdge paths TEST_F(SdfDesignTest, ReadSdfEdgeIopath) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *sdf_path = "/tmp/test_r11_edge_iopath.sdf"; FILE *fp = fopen(sdf_path, "w"); ASSERT_NE(fp, nullptr); fprintf(fp, "(DELAYFILE\n"); fprintf(fp, " (SDFVERSION \"3.0\")\n"); fprintf(fp, " (DESIGN \"top\")\n"); fprintf(fp, " (TIMESCALE 1ns)\n"); fprintf(fp, " (CELL\n"); fprintf(fp, " (CELLTYPE \"DFFHQx4_ASAP7_75t_R\")\n"); fprintf(fp, " (INSTANCE r1)\n"); fprintf(fp, " (DELAY\n"); fprintf(fp, " (ABSOLUTE\n"); fprintf(fp, " (IOPATH (posedge CLK) Q (0.100::0.200) (0.150::0.250))\n"); fprintf(fp, " (IOPATH (negedge CLK) Q (0.110::0.210) (0.160::0.260))\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, ")\n"); fclose(fp); readSdf(sdf_path, nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_); std::remove(sdf_path); } // R11_4: Read SDF with SETUPHOLD combined check // Covers: SdfReader::timingCheckSetupHold path TEST_F(SdfDesignTest, ReadSdfSetupHold) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *sdf_path = "/tmp/test_r11_setuphold.sdf"; FILE *fp = fopen(sdf_path, "w"); ASSERT_NE(fp, nullptr); fprintf(fp, "(DELAYFILE\n"); fprintf(fp, " (SDFVERSION \"3.0\")\n"); fprintf(fp, " (DESIGN \"top\")\n"); fprintf(fp, " (TIMESCALE 1ns)\n"); fprintf(fp, " (CELL\n"); fprintf(fp, " (CELLTYPE \"DFFHQx4_ASAP7_75t_R\")\n"); fprintf(fp, " (INSTANCE r1)\n"); fprintf(fp, " (TIMINGCHECK\n"); fprintf(fp, " (SETUPHOLD D (posedge CLK) (0.050) (0.020))\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, ")\n"); fclose(fp); readSdf(sdf_path, nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_); std::remove(sdf_path); } // R11_5: Read SDF with RECREM combined check // Covers: SdfReader::timingCheckRecRem path TEST_F(SdfDesignTest, ReadSdfRecRem) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *sdf_path = "/tmp/test_r11_recrem.sdf"; FILE *fp = fopen(sdf_path, "w"); ASSERT_NE(fp, nullptr); fprintf(fp, "(DELAYFILE\n"); fprintf(fp, " (SDFVERSION \"3.0\")\n"); fprintf(fp, " (DESIGN \"top\")\n"); fprintf(fp, " (TIMESCALE 1ns)\n"); fprintf(fp, " (CELL\n"); fprintf(fp, " (CELLTYPE \"DFFHQx4_ASAP7_75t_R\")\n"); fprintf(fp, " (INSTANCE r1)\n"); fprintf(fp, " (TIMINGCHECK\n"); fprintf(fp, " (RECREM D (posedge CLK) (0.050) (0.020))\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, ")\n"); fclose(fp); readSdf(sdf_path, nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_); std::remove(sdf_path); } // R11_6: Read SDF with WIDTH check // Covers: SdfReader::timingCheckWidth path TEST_F(SdfDesignTest, ReadSdfWidth) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *sdf_path = "/tmp/test_r11_width.sdf"; FILE *fp = fopen(sdf_path, "w"); ASSERT_NE(fp, nullptr); fprintf(fp, "(DELAYFILE\n"); fprintf(fp, " (SDFVERSION \"3.0\")\n"); fprintf(fp, " (DESIGN \"top\")\n"); fprintf(fp, " (TIMESCALE 1ns)\n"); fprintf(fp, " (CELL\n"); fprintf(fp, " (CELLTYPE \"DFFHQx4_ASAP7_75t_R\")\n"); fprintf(fp, " (INSTANCE r1)\n"); fprintf(fp, " (TIMINGCHECK\n"); fprintf(fp, " (WIDTH (posedge CLK) (0.100))\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, ")\n"); fclose(fp); readSdf(sdf_path, nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_); std::remove(sdf_path); } // R11_7: Read SDF with PERIOD check // Covers: SdfReader::timingCheckPeriod path TEST_F(SdfDesignTest, ReadSdfPeriod) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *sdf_path = "/tmp/test_r11_period.sdf"; FILE *fp = fopen(sdf_path, "w"); ASSERT_NE(fp, nullptr); fprintf(fp, "(DELAYFILE\n"); fprintf(fp, " (SDFVERSION \"3.0\")\n"); fprintf(fp, " (DESIGN \"top\")\n"); fprintf(fp, " (TIMESCALE 1ns)\n"); fprintf(fp, " (CELL\n"); fprintf(fp, " (CELLTYPE \"DFFHQx4_ASAP7_75t_R\")\n"); fprintf(fp, " (INSTANCE r1)\n"); fprintf(fp, " (TIMINGCHECK\n"); fprintf(fp, " (PERIOD (posedge CLK) (1.000))\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, ")\n"); fclose(fp); readSdf(sdf_path, nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_); std::remove(sdf_path); } // R11_8: Read SDF with NOCHANGE check // Covers: SdfReader::timingCheckNochange, notSupported // NOCHANGE is not supported and throws, so we catch the exception TEST_F(SdfDesignTest, ReadSdfNochange) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *sdf_path = "/tmp/test_r11_nochange.sdf"; FILE *fp = fopen(sdf_path, "w"); ASSERT_NE(fp, nullptr); fprintf(fp, "(DELAYFILE\n"); fprintf(fp, " (SDFVERSION \"3.0\")\n"); fprintf(fp, " (DESIGN \"top\")\n"); fprintf(fp, " (TIMESCALE 1ns)\n"); fprintf(fp, " (CELL\n"); fprintf(fp, " (CELLTYPE \"DFFHQx4_ASAP7_75t_R\")\n"); fprintf(fp, " (INSTANCE r1)\n"); fprintf(fp, " (TIMINGCHECK\n"); fprintf(fp, " (NOCHANGE D (posedge CLK) (0.050) (0.020))\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, ")\n"); fclose(fp); // NOCHANGE is not supported and throws an exception EXPECT_THROW( readSdf(sdf_path, nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_), std::exception ); std::remove(sdf_path); } // R11_9: Read SDF with INTERCONNECT delay // Covers: SdfReader::interconnect path TEST_F(SdfDesignTest, ReadSdfInterconnect) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *sdf_path = "/tmp/test_r11_interconnect.sdf"; FILE *fp = fopen(sdf_path, "w"); ASSERT_NE(fp, nullptr); fprintf(fp, "(DELAYFILE\n"); fprintf(fp, " (SDFVERSION \"3.0\")\n"); fprintf(fp, " (DESIGN \"top\")\n"); fprintf(fp, " (TIMESCALE 1ns)\n"); fprintf(fp, " (CELL\n"); fprintf(fp, " (CELLTYPE \"top\")\n"); fprintf(fp, " (INSTANCE)\n"); fprintf(fp, " (DELAY\n"); fprintf(fp, " (ABSOLUTE\n"); fprintf(fp, " (INTERCONNECT u1/Y r3/D (0.010::0.020) (0.015::0.025))\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, " )\n"); fprintf(fp, ")\n"); fclose(fp); readSdf(sdf_path, nullptr, corner, false, false, const_cast(MinMaxAll::all()), sta_); std::remove(sdf_path); } // R11_10: WriteSdf with include_typ=true and no_version=false to cover // the writeHeader path with version TEST_F(SdfDesignTest, WriteSdfWithVersion) { ASSERT_TRUE(design_loaded_); sta_->ensureGraph(); Scene *corner = sta_->cmdScene(); sta_->readSpef("test/reg1_asap7.spef", "test/reg1_asap7.spef", sta_->network()->topInstance(), corner, MinMaxAll::all(), false, false, 1.0f, true); const char *tmpfile = "/tmp/test_r11_sdf_version.sdf"; // no_timestamp=false, no_version=false -> includes version and timestamp sta_->writeSdf(tmpfile, corner, '/', true, 4, false, false, false); FILE *f = fopen(tmpfile, "r"); ASSERT_NE(f, nullptr); fseek(f, 0, SEEK_END); long size = ftell(f); fclose(f); EXPECT_GT(size, 0); std::remove(tmpfile); } } // namespace sta