Skip to content

Commit

Permalink
Merge pull request #41395 from wddgit/testTriggerResults
Browse files Browse the repository at this point in the history
Add unit test for TriggerResults format
  • Loading branch information
cmsbuild authored Apr 29, 2023
2 parents caeae41 + cc027c6 commit 95b4d71
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 0 deletions.
10 changes: 10 additions & 0 deletions DataFormats/Common/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# DataFormats/Common

## `edm::TriggerResults`

The class `edm::TriggerResults` is part of the RAW data, and any changes must be backwards compatible. In order to ensure it can be read by all future CMSSW releases, there is a `TestTriggerResultsFormat` unit test, which makes use of the `TestReadTriggerResults` analyzer and the `TestWriteTriggerResults` producer. The unit test checks that the object can be read properly from

* a file in the same release as it was written
* files written by (some) earlier releases can be read

If the persistent format of class `edm::TriggerResults` gets changed in the future, please adjust the `TestReadTriggerResults` and `TestWriteTriggerResults` modules accordingly. It is important that every member container has some content in this test. Please also add a new file to [https://github.com/cms-data/DataFormats-Common/](https://github.com/cms-data/DataFormats-Common/) repository, and update the `TestTriggerResultsFormat` unit test to read the newly created file. The file name should contain the release or pre-release with which it was written.`
10 changes: 10 additions & 0 deletions DataFormats/Common/test/BuildFile.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
<use name="boost"/>
<use name="cppunit"/>
<use name="DataFormats/Common"/>

<library name="testTriggerResults" file="TestReadTriggerResults.cc,TestWriteTriggerResults.cc">
<flags EDM_PLUGIN="1"/>
<use name="FWCore/Framework"/>
<use name="FWCore/ParameterSet"/>
<use name="FWCore/Utilities"/>
</library>

<test name="TestTriggerResultsFormat" command="TestTriggerResultsFormat.sh"/>

<bin name="testDataFormatsCommon" file="testRunner.cpp,testOwnVector.cc,testOneToOneAssociation.cc,testValueMap.cc,testOneToManyAssociation.cc,testAssociationVector.cc,testAssociationNew.cc,testValueMapNew.cc,testSortedCollection.cc,testRangeMap.cc,testIDVectorMap.cc,ref_t.cppunit.cc,DetSetRefVector_t.cppunit.cc,reftobase_t.cppunit.cc,reftobasevector_t.cppunit.cc,cloningptr_t.cppunit.cc,ptr_t.cppunit.cc,ptrvector_t.cppunit.cc,containermask_t.cppunit.cc,reftobaseprod_t.cppunit.cc,handle_t.cppunit.cc">
</bin>

Expand Down
100 changes: 100 additions & 0 deletions DataFormats/Common/test/TestReadTriggerResults.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// -*- C++ -*-
//
// Package: DataFormats/Common
// Class: TestReadTriggerResults
//
/**\class edmtest::TestReadTriggerResults
Description: Used as part of tests that ensure the TriggerResults
data format can be persistently written and in a subsequent process
read. First, this is done using the current release version. In
addition, the output file of the write process should be saved
permanently each time its format changes. In unit tests, we read
each of those saved files to verify that all future releases can
read RAW data formats and Scouting data formats.
*/
// Original Author: W. David Dagenhart
// Created: 18 April 2023

#include "DataFormats/Common/interface/TriggerResults.h"
#include "FWCore/Framework/interface/global/EDAnalyzer.h"
#include "FWCore/Framework/interface/Event.h"
#include "FWCore/Framework/interface/Frameworkfwd.h"
#include "FWCore/Framework/interface/MakerMacros.h"
#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
#include "FWCore/ParameterSet/interface/ParameterSet.h"
#include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
#include "FWCore/Utilities/interface/EDGetToken.h"
#include "FWCore/Utilities/interface/Exception.h"
#include "FWCore/Utilities/interface/InputTag.h"

#include <string>
#include <vector>

namespace edmtest {

class TestReadTriggerResults : public edm::global::EDAnalyzer<> {
public:
TestReadTriggerResults(edm::ParameterSet const&);
void analyze(edm::StreamID, edm::Event const&, edm::EventSetup const&) const override;
void throwWithMessage(const char*) const;
static void fillDescriptions(edm::ConfigurationDescriptions&);

private:
std::string expectedParameterSetID_;
std::vector<std::string> expectedNames_;
std::vector<unsigned int> expectedHLTStates_;
std::vector<unsigned int> expectedModuleIndexes_;
edm::EDGetTokenT<edm::TriggerResults> triggerResultsToken_;
};

TestReadTriggerResults::TestReadTriggerResults(edm::ParameterSet const& iPSet)
: expectedParameterSetID_(iPSet.getParameter<std::string>("expectedParameterSetID")),
expectedNames_(iPSet.getParameter<std::vector<std::string>>("expectedNames")),
expectedHLTStates_(iPSet.getParameter<std::vector<unsigned int>>("expectedHLTStates")),
expectedModuleIndexes_(iPSet.getParameter<std::vector<unsigned int>>("expectedModuleIndexes")),
triggerResultsToken_(consumes(iPSet.getParameter<edm::InputTag>("triggerResultsTag"))) {}

void TestReadTriggerResults::analyze(edm::StreamID, edm::Event const& iEvent, edm::EventSetup const&) const {
auto const& triggerResults = iEvent.get(triggerResultsToken_);
std::string parameterSetID;
triggerResults.parameterSetID().toString(parameterSetID);
if (parameterSetID != expectedParameterSetID_) {
throwWithMessage("parameterSetID does not match expected value");
}
if (triggerResults.getTriggerNames() != expectedNames_) {
throwWithMessage("names vector does not include expected values");
}
if (expectedHLTStates_.size() != expectedModuleIndexes_.size()) {
throwWithMessage(
"test configuration error, expectedHLTStates and expectedModuleIndexes should have the same size");
}
if (triggerResults.size() != expectedHLTStates_.size()) {
throwWithMessage("paths has unexpected size");
}
for (unsigned int i = 0; i < expectedHLTStates_.size(); ++i) {
if (static_cast<unsigned int>(triggerResults.state(i)) != expectedHLTStates_[i]) {
throwWithMessage("state has unexpected value");
}
if (triggerResults.index(i) != expectedModuleIndexes_[i]) {
throwWithMessage("module index has unexpected value");
}
}
}

void TestReadTriggerResults::throwWithMessage(const char* msg) const {
throw cms::Exception("TestFailure") << "TestReadTriggerResults::analyze, " << msg;
}

void TestReadTriggerResults::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
edm::ParameterSetDescription desc;
desc.add<std::string>("expectedParameterSetID");
desc.add<std::vector<std::string>>("expectedNames");
desc.add<std::vector<unsigned int>>("expectedHLTStates");
desc.add<std::vector<unsigned int>>("expectedModuleIndexes");
desc.add<edm::InputTag>("triggerResultsTag");
descriptions.addDefault(desc);
}
} // namespace edmtest

using edmtest::TestReadTriggerResults;
DEFINE_FWK_MODULE(TestReadTriggerResults);
19 changes: 19 additions & 0 deletions DataFormats/Common/test/TestTriggerResultsFormat.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/sh -ex

function die { echo $1: status $2 ; exit $2; }

LOCAL_TEST_DIR=${SCRAM_TEST_PATH}

cmsRun ${LOCAL_TEST_DIR}/create_triggerresults_test_file_cfg.py || die 'Failure using create_triggerresults_test_file_cfg.py' $?

file=testTriggerResults.root

cmsRun ${LOCAL_TEST_DIR}/test_readTriggerResults_cfg.py "$file" || die "Failure using test_readTriggerResults_cfg.py $file" $?

oldFiles="testTriggerResults_CMSSW_13_0_0.root testTriggerResults_CMSSW_13_1_0_pre3.root"
for file in $oldFiles; do
inputfile=$(edmFileInPath DataFormats/Common/data/$file) || die "Failure edmFileInPath DataFormats/Common/data/$file" $?
cmsRun ${LOCAL_TEST_DIR}/test_readTriggerResults_cfg.py "$inputfile" || die "Failed to read old file $file" $?
done

exit 0
87 changes: 87 additions & 0 deletions DataFormats/Common/test/TestWriteTriggerResults.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// -*- C++ -*-
//
// Package: DataFormats/Common
// Class: TestWriteTriggerResults
//
/**\class edmtest::TestWriteTriggerResults
Description: Used as part of tests that ensure the TriggerResults
data format can be persistently written and in a subsequent process
read. First, this is done using the current release version. In
addition, the output file of the write process should be saved
permanently each time its format changes. In unit tests, we read
each of those saved files to verify that all future releases can
read all versions of RAW data formats and Scouting data formats.
*/
// Original Author: W. David Dagenhart
// Created: 20 April 2023

#include "DataFormats/Common/interface/HLTGlobalStatus.h"
#include "DataFormats/Common/interface/TriggerResults.h"
#include "DataFormats/Provenance/interface/ParameterSetID.h"
#include "FWCore/Framework/interface/global/EDProducer.h"
#include "FWCore/Framework/interface/Event.h"
#include "FWCore/Framework/interface/Frameworkfwd.h"
#include "FWCore/Framework/interface/MakerMacros.h"
#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
#include "FWCore/ParameterSet/interface/ParameterSet.h"
#include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
#include "FWCore/Utilities/interface/EDPutToken.h"

#include <cassert>
#include <memory>
#include <string>
#include <vector>

namespace edmtest {

class TestWriteTriggerResults : public edm::global::EDProducer<> {
public:
TestWriteTriggerResults(edm::ParameterSet const&);
void produce(edm::StreamID, edm::Event&, edm::EventSetup const&) const override;
static void fillDescriptions(edm::ConfigurationDescriptions&);

private:
std::string parameterSetID_;
std::vector<std::string> names_;
std::vector<unsigned int> hltStates_;
std::vector<unsigned int> moduleIndexes_;
edm::EDPutTokenT<edm::TriggerResults> triggerResultsPutToken_;
};

TestWriteTriggerResults::TestWriteTriggerResults(edm::ParameterSet const& iPSet)
: parameterSetID_(iPSet.getParameter<std::string>("parameterSetID")),
names_(iPSet.getParameter<std::vector<std::string>>("names")),
hltStates_(iPSet.getParameter<std::vector<unsigned int>>("hltStates")),
moduleIndexes_(iPSet.getParameter<std::vector<unsigned int>>("moduleIndexes")),
triggerResultsPutToken_(produces()) {}

void TestWriteTriggerResults::produce(edm::StreamID, edm::Event& iEvent, edm::EventSetup const&) const {
edm::HLTGlobalStatus hltGlobalStatus(hltStates_.size());
for (unsigned int i = 0; i < hltStates_.size(); ++i) {
assert(i < moduleIndexes_.size());
hltGlobalStatus[i] = edm::HLTPathStatus(static_cast<edm::hlt::HLTState>(hltStates_[i]), moduleIndexes_[i]);
}
edm::ParameterSetID parameterSetID(parameterSetID_);
std::unique_ptr<edm::TriggerResults> result;
if (names_.empty()) {
// names_ will always be empty except in extremely old data or monte carlo files
result = std::make_unique<edm::TriggerResults>(hltGlobalStatus, parameterSetID);
} else {
// If names is not empty, the ParameterSetID is not set and default constructed
result = std::make_unique<edm::TriggerResults>(hltGlobalStatus, names_);
}
iEvent.put(triggerResultsPutToken_, std::move(result));
}

void TestWriteTriggerResults::fillDescriptions(edm::ConfigurationDescriptions& descriptions) {
edm::ParameterSetDescription desc;
desc.add<std::string>("parameterSetID");
desc.add<std::vector<std::string>>("names");
desc.add<std::vector<unsigned int>>("hltStates");
desc.add<std::vector<unsigned int>>("moduleIndexes");
descriptions.addDefault(desc);
}
} // namespace edmtest

using edmtest::TestWriteTriggerResults;
DEFINE_FWK_MODULE(TestWriteTriggerResults);
27 changes: 27 additions & 0 deletions DataFormats/Common/test/create_triggerresults_test_file_cfg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import FWCore.ParameterSet.Config as cms

process = cms.Process("PROD")

process.load("FWCore.MessageService.MessageLogger_cfi")

process.source = cms.Source("EmptySource")
process.maxEvents.input = 1

process.triggerResultsProducer = cms.EDProducer("TestWriteTriggerResults",
# Test values below are meaningless. We just make sure when we read
# we get the same values.
parameterSetID = cms.string('8b99d66b6c3865c75e460791f721202d'),
# names should normally be empty. Only extremely old data or
# has names filled and not empty. If it is not empty, the
# ParameterSetID is ignored and left default constructed.
names = cms.vstring(),
hltStates = cms.vuint32(0, 1, 2, 3),
moduleIndexes = cms.vuint32(11, 21, 31, 41)
)

process.out = cms.OutputModule("PoolOutputModule",
fileName = cms.untracked.string('testTriggerResults.root')
)

process.path = cms.Path(process.triggerResultsProducer)
process.endPath = cms.EndPath(process.out)
23 changes: 23 additions & 0 deletions DataFormats/Common/test/test_readTriggerResults_cfg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import FWCore.ParameterSet.Config as cms
import sys

process = cms.Process("READ")

process.source = cms.Source("PoolSource", fileNames = cms.untracked.vstring("file:"+sys.argv[2]))
process.maxEvents.input = 1

process.testReadTriggerResults = cms.EDAnalyzer("TestReadTriggerResults",
triggerResultsTag = cms.InputTag("triggerResultsProducer", "", "PROD"),
expectedParameterSetID = cms.string('8b99d66b6c3865c75e460791f721202d'),
expectedNames = cms.vstring(),
expectedHLTStates = cms.vuint32(0, 1, 2, 3),
expectedModuleIndexes = cms.vuint32(11, 21, 31, 41)
)

process.out = cms.OutputModule("PoolOutputModule",
fileName = cms.untracked.string('testTriggerResults2.root')
)

process.path = cms.Path(process.testReadTriggerResults)

process.endPath = cms.EndPath(process.out)

0 comments on commit 95b4d71

Please sign in to comment.