Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2025-06-17 01:30:10

0001 #include <algorithm>
0002 #include <cassert>
0003 #include <iterator>
0004 #include <ostream>
0005 #include <stdexcept>
0006 #include <vector>
0007 
0008 #include <catch.hpp>
0009 
0010 #include "DataFormats/Common/interface/DetSetVector.h"
0011 #include "DataFormats/Common/interface/Ref.h"
0012 #include "DataFormats/Common/interface/TestHandle.h"
0013 #include "DataFormats/Provenance/interface/ProductID.h"
0014 
0015 using namespace edm;
0016 
0017 //------------------------------------------------------
0018 // This is a sample VALUE class, almost the simplest possible.
0019 //------------------------------------------------------
0020 
0021 struct Empty {};
0022 
0023 template <typename BASE>
0024 class ValueWithKeyT : public BASE {
0025 public:
0026   // VALUES must be default constructible
0027   ValueWithKeyT() : d_(0.0) {}
0028 
0029   // VALUES must be destructible
0030   ~ValueWithKeyT() = default;
0031 
0032   // If VALUES provide a sort_key() const method, it will be used
0033   // to generate keys once, instead of calling operator<.
0034   double sort_key() const { return d_; }
0035 
0036   // VALUES must be LessThanComparable
0037   bool operator<(ValueWithKeyT const& other) const { return sort_key() < other.sort_key(); }
0038 
0039   // The stuff below is all implementation detail, and not
0040   // required by the concept VALUE.
0041 
0042   // This constructor is used for testing; it is not required by the
0043   // concept VALUE.
0044   explicit ValueWithKeyT(double d) : d_(d) {}
0045 
0046   // This access function is used for testing; it is not required by
0047   // the concept VALUE.
0048   double val() const { return d_; }
0049 
0050 private:
0051   double d_;
0052 };
0053 
0054 typedef edm::DoNotSortUponInsertion DNS;
0055 
0056 template <>
0057 double ValueWithKeyT<DNS>::sort_key() const {
0058   throw std::logic_error("You can't sort me!");
0059 }
0060 
0061 typedef ValueWithKeyT<Empty> Value;
0062 //typedef ValueWithKeyT<DNS> Value; // NoSort;
0063 
0064 //------------------------------------------------------
0065 // The stream insertion operator is not required; it is used here
0066 // for diagnostic output.
0067 //
0068 // It is inline to avoid multiple definition problems. Note that this
0069 // cc file is *not* a compilation unit; it is actually an include
0070 // file, which the build system combines with others to create a
0071 // compilation unit.
0072 //
0073 //------------------------------------------------------
0074 
0075 template <typename BASE>
0076 std::ostream& operator<<(std::ostream& os, ValueWithKeyT<BASE> const& v) {
0077   os << " val: " << v.val();
0078   return os;
0079 }
0080 
0081 typedef edm::DetSetVector<Value> coll_type;
0082 typedef coll_type::detset detset;
0083 
0084 void check_outer_collection_order(coll_type const& c) {
0085   if (c.size() < 2)
0086     return;
0087   coll_type::const_iterator i = c.begin();
0088   coll_type::const_iterator e = c.end();
0089   // Invariant: sequence from prev to i is correctly ordered
0090   coll_type::const_iterator prev(i);
0091   ++i;
0092   for (; i != e; ++i, ++prev) {
0093     // We don't use CPPUNIT_ASSERT because it gives us grossly
0094     // insufficient context if a failure occurs.
0095     REQUIRE(prev->id < i->id);
0096   }
0097 }
0098 
0099 void check_inner_collection_order(detset const& d) {
0100   if (d.data.size() < 2)
0101     return;
0102   detset::const_iterator i = d.data.begin();
0103   detset::const_iterator e = d.data.end();
0104   // Invariant: sequence from prev to i is correctly ordered
0105   detset::const_iterator prev(i);
0106   ++i;
0107   for (; i != e; ++i, ++prev) {
0108     // We don't use CPPUNIT_ASSERT because it gives us grossly
0109     // insufficient context if a failure occurs.
0110     //
0111     // We don't check that *prev < *i because they might be equal.
0112     // We don't want to require an op<= or op==.
0113     REQUIRE(!(*i < *prev));
0114   }
0115 }
0116 
0117 void printDetSet(detset const& ds, std::ostream& os) {
0118   os << "size: " << ds.data.size() << '\n' << "values: ";
0119   std::copy(ds.data.begin(), ds.data.end(), std::ostream_iterator<detset::value_type>(os, " "));
0120 }
0121 
0122 void sanity_check(coll_type const& c) {
0123   check_outer_collection_order(c);
0124   for (coll_type::const_iterator i = c.begin(), e = c.end(); i != e; ++i) {
0125     //       printDetSet(*i, std::cerr);
0126     //       std::cerr << '\n';
0127     check_inner_collection_order(*i);
0128   }
0129 }
0130 
0131 void check_ids(coll_type const& c) {
0132   // Long way to get all ids...
0133   std::vector<det_id_type> all_ids;
0134   for (coll_type::const_iterator i = c.begin(), e = c.end(); i != e; ++i) {
0135     all_ids.push_back(i->id);
0136   }
0137   REQUIRE(c.size() == all_ids.size());
0138 
0139   std::vector<det_id_type> nice_ids;
0140   c.getIds(nice_ids);
0141   REQUIRE(all_ids == nice_ids);
0142 }
0143 
0144 namespace {
0145   template <typename T>
0146   struct DSVGetter : edm::EDProductGetter {
0147     DSVGetter() : edm::EDProductGetter(), prod_(nullptr) {}
0148     WrapperBase const* getIt(ProductID const&) const override { return prod_; }
0149 
0150     std::optional<std::tuple<edm::WrapperBase const*, unsigned int>> getThinnedProduct(ProductID const&,
0151                                                                                        unsigned int) const override {
0152       return std::nullopt;
0153     }
0154 
0155     void getThinnedProducts(ProductID const& pid,
0156                             std::vector<WrapperBase const*>& wrappers,
0157                             std::vector<unsigned int>& keys) const override {}
0158 
0159     edm::OptionalThinnedKey getThinnedKeyFrom(ProductID const&, unsigned int, ProductID const&) const override {
0160       return std::monostate{};
0161     }
0162 
0163     unsigned int transitionIndex_() const override { return 0U; }
0164 
0165     edm::Wrapper<T> const* prod_;
0166   };
0167 }  // namespace
0168 
0169 TEST_CASE("test DetSetVector with sort_key", "[DetSetVector]") {
0170   SECTION("detsetTest") {
0171     //std::cerr << "\nStart DetSetVector_t detsetTest()\n";
0172     detset d;
0173     Value v1(1.1);
0174     Value v2(2.2);
0175     d.id = edm::det_id_type(3);
0176     d.data.push_back(v1);
0177     d.data.push_back(v2);
0178     edm::detail::sort_by_key(d.data.begin(), d.data.end(), [](const Value& v) { return v.sort_key(); });
0179     check_inner_collection_order(d);
0180     //std::cerr << "\nEnd DetSetVector_t detsetTest()\n";
0181   }
0182 
0183   SECTION("refTest") {
0184     coll_type c;
0185     detset d3;
0186     Value v1(1.1);
0187     Value v2(2.2);
0188     d3.id = edm::det_id_type(3);
0189     d3.data.push_back(v1);
0190     d3.data.push_back(v2);
0191     c.insert(d3);
0192     detset d1;
0193     Value v1a(4.1);
0194     Value v2a(3.2);
0195     d1.id = edm::det_id_type(1);
0196     d1.data.push_back(v1a);
0197     d1.data.push_back(v2a);
0198     c.insert(d1);
0199     c.post_insert();
0200 
0201     auto pC = std::make_unique<coll_type>(c);
0202     edm::Wrapper<coll_type> wrapper(std::move(pC));
0203     DSVGetter<coll_type> theGetter;
0204     theGetter.prod_ = &wrapper;
0205 
0206     typedef edm::Ref<coll_type, detset> RefDetSet;
0207     typedef edm::Ref<coll_type, Value> RefDet;
0208 
0209     {
0210       RefDetSet refSet(edm::ProductID(1, 1), 0, &theGetter);
0211       REQUIRE((!(d1 < *refSet) && !(*refSet < d1)));
0212     }
0213     {
0214       RefDetSet refSet(edm::ProductID(1, 1), 1, &theGetter);
0215       REQUIRE((!(d3 < *refSet) && !(*refSet < d3)));
0216     }
0217     {
0218       RefDet refDet(edm::ProductID(1, 1), RefDet::key_type(3, 0), &theGetter);
0219       REQUIRE((!(v1 < *refDet) && !(*refDet < v1)));
0220     }
0221 
0222     {
0223       TestHandle<coll_type> pc2(&c, ProductID(1, 1));
0224       RefDet refDet = makeRefToDetSetVector(pc2, det_id_type(3), c[3].data.begin());
0225       REQUIRE((!(v1 < *refDet) && !(*refDet < v1)));
0226     }
0227 
0228     using namespace Catch::Matchers;
0229     SECTION("bad detid") {
0230       TestHandle<coll_type> pc2(&c, ProductID(1, 1));
0231       REQUIRE_THROWS_MATCHES(
0232           makeRefToDetSetVector(pc2, det_id_type(12), c[3].data.begin()),
0233           edm::Exception,
0234           Predicate<edm::Exception>(
0235               [](auto const& iExcept) { return iExcept.categoryCode() == edm::errors::InvalidReference; },
0236               "Exception has wrong category"));
0237     }
0238 
0239     SECTION("bad iterator") {
0240       TestHandle<coll_type> pc2(&c, ProductID(1, 1));
0241       REQUIRE_THROWS_MATCHES(
0242           makeRefToDetSetVector(pc2, det_id_type(1), c[3].data.begin()),
0243           edm::Exception,
0244           Predicate<edm::Exception>([](auto const& x) { return x.categoryCode() == edm::errors::InvalidReference; },
0245                                     "Exception has wrong category"));
0246     }
0247   }
0248 
0249   SECTION("work") {
0250     coll_type c1;
0251     c1.post_insert();
0252     sanity_check(c1);
0253     REQUIRE(c1.size() == 0);
0254     REQUIRE(c1.empty());
0255 
0256     coll_type c2(c1);
0257     REQUIRE(c2.size() == c1.size());
0258     sanity_check(c2);
0259     coll_type c;
0260     sanity_check(c);
0261     {
0262       detset d;
0263       Value v1(1.1);
0264       Value v2(2.2);
0265       d.id = edm::det_id_type(3);
0266       d.data.push_back(v1);
0267       d.data.push_back(v2);
0268       c.insert(d);
0269       c.post_insert();
0270     }
0271     sanity_check(c);
0272     REQUIRE(c.size() == 1);
0273     {
0274       detset d;
0275       Value v1(4.1);
0276       Value v2(3.2);
0277       d.id = edm::det_id_type(1);
0278       d.data.push_back(v1);
0279       d.data.push_back(v2);
0280       c.insert(d);
0281       c.post_insert();
0282     }
0283     sanity_check(c);
0284     REQUIRE(c.size() == 2);
0285 
0286     {
0287       detset d;
0288       Value v1(1.1);
0289       Value v2(1.2);
0290       Value v3(2.2);
0291       d.id = edm::det_id_type(10);
0292       d.data.push_back(v3);
0293       d.data.push_back(v2);
0294       d.data.push_back(v1);
0295       c.insert(d);
0296       c.post_insert();
0297     }
0298     sanity_check(c);
0299     REQUIRE(c.size() == 3);
0300 
0301     coll_type another;
0302     c.swap(another);
0303     REQUIRE(c.empty());
0304     REQUIRE(another.size() == 3);
0305     sanity_check(c);
0306     sanity_check(another);
0307 
0308     c.swap(another);
0309     REQUIRE(c.size() == 3);
0310 
0311     {
0312       // We should not find anything with ID=11
0313       coll_type::iterator i = c.find(edm::det_id_type(11));
0314       REQUIRE(i == c.end());
0315 
0316       coll_type::const_iterator ci = static_cast<coll_type const&>(c).find(edm::det_id_type(11));
0317       REQUIRE(ci == c.end());
0318     }
0319     {
0320       // We should find  ID=10
0321       coll_type::iterator i = c.find(edm::det_id_type(10));
0322       REQUIRE(i != c.end());
0323       REQUIRE(i->id == 10);
0324       REQUIRE(i->data.size() == 3);
0325     }
0326     using namespace Catch::Matchers;
0327     {
0328       // We should not find ID=100; op[] should throw.
0329       SECTION("op[] should throw") {
0330         REQUIRE_THROWS_MATCHES(
0331             c[edm::det_id_type(100)],
0332             edm::Exception,
0333             Predicate<edm::Exception>([](auto const& x) { return x.categoryCode() == edm::errors::InvalidReference; },
0334                                       "Exception has wrong category"));
0335       }
0336     }
0337 
0338     {
0339       // We should not find ID=100; op[] should throw.
0340       SECTION("coll_type op[] should throw") {
0341         REQUIRE_THROWS_MATCHES(
0342             static_cast<coll_type const&>(c)[edm::det_id_type(100)],
0343             edm::Exception,
0344             Predicate<edm::Exception>([](auto const& x) { return x.categoryCode() == edm::errors::InvalidReference; },
0345                                       "Exception has wrong category"));
0346       }
0347     }
0348     {
0349       // We should find id = 3
0350       coll_type const& rc = c;
0351       coll_type::const_reference r = rc[3];
0352       REQUIRE(r.id == edm::det_id_type(3));
0353       REQUIRE(r.data.size() == 2);
0354 
0355       coll_type::reference r2 = c[3];
0356       REQUIRE(r2.id == edm::det_id_type(3));
0357       REQUIRE(r2.data.size() == 2);
0358     }
0359 
0360     {
0361       // We should not find id = 17, but a new empty DetSet should be
0362       // inserted, carrying the correct DetId.
0363       coll_type::size_type oldsize = c.size();
0364       coll_type::reference r = c.find_or_insert(edm::det_id_type(17));
0365       coll_type::size_type newsize = c.size();
0366       REQUIRE(newsize > oldsize);
0367       REQUIRE(newsize == (oldsize + 1));
0368       REQUIRE(r.id == edm::det_id_type(17));
0369       REQUIRE(r.data.size() == 0);
0370       r.data.push_back(Value(10.1));
0371       r.data.push_back(Value(9.1));
0372       r.data.push_back(Value(4.0));
0373       r.data.push_back(Value(4.0));
0374       c.post_insert();
0375       sanity_check(c);
0376     }
0377     {
0378       // Make sure we can swap in a vector.
0379       unsigned int const numDetSets = 20;
0380       unsigned int const detSetSize = 14;
0381       std::vector<detset> v;
0382       for (unsigned int i = 0; i < numDetSets; ++i) {
0383         detset d(i);
0384         for (unsigned int j = 0; j < detSetSize; ++j) {
0385           d.data.push_back(Value(100 * i + 1.0 / j));
0386         }
0387         v.push_back(d);
0388       }
0389       REQUIRE(v.size() == numDetSets);
0390       coll_type c3(v);
0391       c3.post_insert();
0392       REQUIRE(v.size() == 0);
0393       REQUIRE(c3.size() == numDetSets);
0394       sanity_check(c3);
0395 
0396       coll_type c4;
0397       c4 = c3;
0398       REQUIRE(c4.size() == numDetSets);
0399       sanity_check(c3);
0400       sanity_check(c4);
0401 
0402       check_ids(c3);
0403     }
0404   }
0405 }