Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TestSupport.cxx
Go to the documentation of this file.
1/// \file
2/// \brief This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
3///
4/// \author Stephan Hageboeck <stephan.hageboeck@cern.ch>
5
6/*************************************************************************
7 * Copyright (C) 1995-2021, Rene Brun and Fons Rademakers. *
8 * All rights reserved. *
9 * *
10 * For the licensing terms see $ROOTSYS/LICENSE. *
11 * For the list of contributors see $ROOTSYS/README/CREDITS. *
12 *************************************************************************/
13
14#include "ROOT/TestSupport.hxx"
15
16#include "gtest/gtest.h"
17
18#include <algorithm>
19#include <cstring>
20#include <iostream>
21#include <iomanip>
22
23namespace ROOT {
24namespace TestSupport {
25
26/// Error handler for gtests that generates failures for every received diagnostic > kInfo when this file is linked to.
27static struct ForbidDiagnostics {
32
36
37 /// Diagnostic handler that's installed for all google tests.
38 /// It will generate a test failure when a diagnostic message is issued.
39 static void handler(int level, bool abort,
40 const char *location,
41 const char *msg) {
42 if (level <= gErrorIgnoreLevel) return;
43 if (level <= kInfo) {
44 std::cerr << "ROOT::TestSupport::ForbidDiagnostics::handler(): Diagnostic in '" << location << "':\n"
45 << msg << std::endl;
46 return;
47 }
48
49 if (abort) {
50 std::cerr << "ROOT::TestSupport::ForbidDiagnostics::handler(): Forced to abort because of diagnostic with severity "
51 << level << " in '" << location << "' reading '" << msg << "'\n";
52 ::abort();
53 }
54
55 // FIXME: Windows has problem finding PCMs.
56 if (level == kError && strcmp(location, "TCling::LoadPCM") == 0 && strstr(msg, "file does not exist") != nullptr) {
57 std::cerr << "Error in " << location << " " << msg << std::endl;
58 return;
59 }
60
61 // FIXME: RNTuple warns that it's in beta stage.
62 if (level == kWarning && strstr(msg, "Merging RNTuples is experimental") != nullptr) {
63 std::cerr << "Warning in " << location << " " << msg << std::endl;
64 return;
65 }
66
67 // FIXME: DOAS backend is exprimental.
68 if (level == kWarning
69 && strstr(msg, "The DAOS backend is experimental and still under development") != nullptr) {
70 std::cerr << "Warning in " << location << " " << msg << std::endl;
71 return;
72 }
73
74 // FIXME: RooNaNPacker warns about not being implemented for big endian
75 if (level == kWarning
76 && strcmp(msg, "Fast recovery from undefined function values only implemented for little-endian machines. If necessary, request an extension of functionality on https://root.cern") == 0) {
77 std::cerr << "Warning in " << location << " " << msg << std::endl;
78 return;
79 }
80
81 if (level == kWarning && strcmp(location, "RIoUring") == 0 &&
82 strstr(msg, "io_uring is unexpectedly not available because:") != nullptr) {
83 std::cerr << "Warning in " << location << " " << msg << std::endl;
84 return;
85 }
86
87 if (level == kWarning && strcmp(location, "RRawFileUnix") == 0 &&
88 strcmp(msg, "io_uring setup failed, falling back to blocking I/O in ReadV") == 0) {
89 std::cerr << "Warning in " << location << " " << msg << std::endl;
90 return;
91 }
92
93 FAIL() << "Received unexpected diagnostic of severity "
94 << level
95 << " at '" << location << "' reading '" << msg << "'.\n"
96 << "Suppress those using ROOT/TestSupport.hxx";
97 }
98
101
102
103CheckDiagsRAII * CheckDiagsRAII::sActiveInstance = nullptr;
104
108 gInterpreter->ReportDiagnosticsToErrorHandler(/*enable=*/false);
109
110 for (const auto &diag : fExpectedDiags) {
111 if (!diag.optional && diag.receivedCount < 1) {
112 ADD_FAILURE() << "ROOT::TestSupport::CheckDiagsRAII: Expected diagnostic message missing:\n" << diag;
113 }
114 }
115
116 for (const auto &diag : fUnexpectedDiags) {
117 ADD_FAILURE() << "ROOT::TestSupport::CheckDiagsRAII: Unexpected diagnostic message:\n" << diag;
118 }
119}
120
121/// Search list of expected diagnostics for given arguments, and increase the receivedCount.
122/// If no matching predefined diagnostic is found, this will trigger an unexpected diagnostic error on exit.
123void CheckDiagsRAII::checkDiag(int severity, const char * location, const char * msg) {
124 // Check that this received diagnostics was expected
125 const std::string message = msg;
126 const auto expectedMatch =
127 std::find_if(fExpectedDiags.begin(), fExpectedDiags.end(), [=](const Diag_t &expectedDiag) {
128 return expectedDiag.severity == severity
129 && strstr(location, expectedDiag.location.c_str()) != nullptr
130 && (expectedDiag.matchFullString ? expectedDiag.message == message : (message.find(expectedDiag.message) != std::string::npos));
131 });
132
134 expectedMatch->receivedCount += 1;
135 } else if (severity <= kInfo) {
136 fOldErrorHandler(severity, false, location, msg);
137 } else {
138 fUnexpectedDiags.push_back({severity, location, std::move(message)});
139 }
140}
141
142std::ostream &operator<<(std::ostream &stream, CheckDiagsRAII::Diag_t const &diag)
143{
144 std::map<int, std::string> dict = {
145 {kInfo, "kInfo"}, {kWarning, "kWarning"}, {kError, "kError"}, {kSysError, "kSysError"}};
146
147 constexpr auto indent = 15;
148 stream << std::right << std::setw(indent) << "severity: " << dict[diag.severity];
149 if (diag.receivedCount >= 0) {
150 stream << "\n"
151 << std::setw(indent) << "received: " << diag.receivedCount << " times ("
152 << (diag.optional ? "optional" : "required") << ", " << (diag.matchFullString ? "fullMatch" : "subMatch")
153 << ")\t";
154 }
155 stream << "\n"
156 << std::setw(indent) << "origin: " << '"' << diag.location << "\""
157 << "\n"
158 << std::setw(indent) << "message: " << diag.message << "\n";
159
160 return stream;
161}
162} } //ROOT::TestSupport
static void indent(ostringstream &buf, int indent_level)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
ErrorHandlerFunc_t GetErrorHandler()
Returns the current error handler function.
Definition TError.cxx:102
void(* ErrorHandlerFunc_t)(int level, Bool_t abort, const char *location, const char *msg)
Definition TError.h:71
Int_t gErrorIgnoreLevel
errors with level below this value will be ignored. Default is kUnset.
Definition TError.cxx:33
constexpr Int_t kSysError
Definition TError.h:49
ErrorHandlerFunc_t SetErrorHandler(ErrorHandlerFunc_t newhandler)
Set an errorhandler function. Returns the old handler.
Definition TError.cxx:92
#define gInterpreter
The file contains facilities allowing easier writing of in-tree unit tests.
const_iterator end() const
ErrorHandlerFunc_t const fOldErrorHandler
Last active handler in case handlers are nested.
static CheckDiagsRAII * sActiveInstance
Last active error handler function.
std::vector< Diag_t > fUnexpectedDiags
std::vector< Diag_t > fExpectedDiags
void checkDiag(int severity, const char *location, const char *msg)
Check all received diags against list of expected ones.
CheckDiagsRAII *const fOldInstance
static struct ROOT::TestSupport::ForbidDiagnostics noDiagCheckerInstance
std::ostream & operator<<(std::ostream &stream, CheckDiagsRAII::Diag_t const &diag)
Namespace for new ROOT classes and functions.
@ kInfo
Informational messages; used for instance for tracing.
@ kError
An error.
@ kWarning
Warnings about likely unexpected behavior.
Error handler for gtests that generates failures for every received diagnostic > kInfo when this file...
ErrorHandlerFunc_t const sOldErrorHandler
static void handler(int level, bool abort, const char *location, const char *msg)
Diagnostic handler that's installed for all google tests.