// ball_testobserver.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BALL_TESTOBSERVER #define INCLUDED_BALL_TESTOBSERVER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide an instrumented observer for testing. // //@CLASSES: // ball::TestObserver: instrumented observer for testing // //@SEE_ALSO: ball_record, ball_context, ball_observer // //@DESCRIPTION: This component provides a concrete implementation of the // 'ball::Observer' protocol, 'ball::TestObserver', that is instrumented for // testing systems that use 'ball::Observer' objects. //.. // ( ball::TestObserver ) // | static numInstances // | ctor // | setVerbose // | id // | lastPublishedContext // | lastPublishedRecord // | numPublishedRecords // | numReleases // V // ( ball::Observer ) // dtor // publish // releaseRecords //.. // 'ball::TestObserver' ascribes to each instance (within a process) a unique // integer identifier (accessible via the 'id' method), and each instance keeps // a count of the total number of records that it has published (accessible via // the 'numPublishedRecords' method). In addition, the test observer maintains // a copy of the record and context data from the most recent call to // 'publish'; that information is available via the accessors // 'lastPublishedRecord' and 'lastPublishedContext'. // ///Thread Safety ///------------- // The 'ball::TestObserver' provides a 'publish' method that is *thread-safe*, // meaning that the test observer may be used to log records from multiple // threads. However, the 'ball::TestObserver' accessors 'lastPublished' method // and 'lastPublishedContext' provide references to internal data structures // (for backwards compatibility) and are therefore *not* *thread-safe*. // ///Verbose Mode ///------------ // The 'publish' method, in addition to making a local copy of the published // data for future inspection, is capable of printing an appropriate diagnostic // message to the 'ostream' held by the test observer instance. By default, // this printing is suppressed, but the behavior can be altered by calling the // 'setVerbose' method. A call to 'setVerbose' with a non-zero argument (e.g., // 'setVerbose(1)') places the object in verbose mode, which enables diagnostic // printing. A subsequent call of 'setVerbose(0)' restores the default "quiet" // behavior. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Basic Usage /// - - - - - - - - - - - // The following snippets of code illustrate the basic usage of testing // observer objects. The example focuses on the individual features that are // useful for testing larger systems (not shown) that use observers derived // from 'ball::Observer'. // // First create a 'ball::Record' object 'record' and a 'ball::Context' object // 'context'. Note that the default values for these objects (or their // contained objects) are perfectly suitable for testing purposes, since the // testing observer's purpose is simply to report what has been presented to // its 'publish' method. //.. // ball::RecordAttributes attributes; // ball::UserFields fieldValues; // ball::Context context; // // bslma::Allocator *ga = bslma::Default::globalAllocator(0); // const bsl::shared_ptr<const ball::Record> // record(new (*ga) ball::Record(attributes, fieldValues, ga), ga); //.. // Next, create three test observers 'to1', to2', and 'to3', each with // 'bsl::cout' as the held stream. Note that each instance will be given a // unique integer identifier by the constructor. //.. // assert(0 == ball::TestObserver::numInstances()); // // ball::TestObserver to1(&bsl::cout); // assert(1 == to1.id()); // assert(1 == ball::TestObserver::numInstances()); // // ball::TestObserver to2(&bsl::cout); // assert(2 == to2.id()); // assert(2 == ball::TestObserver::numInstances()); // // ball::TestObserver to3(&bsl::cout); // assert(3 == to3.id()); // assert(3 == ball::TestObserver::numInstances()); // // assert(0 == to1.numPublishedRecords()); // assert(0 == to2.numPublishedRecords()); // assert(0 == to3.numPublishedRecords()); //.. // Finally, set 'to1' to "verbose mode" and publish 'record' and 'context' to // 'to1' and also to 'to2', but not to 'to3'. //.. // to1.setVerbose(1); // to1.publish(record, context); // assert(1 == to1.numPublishedRecords()); // // to2.publish(record, context); // assert(1 == to2.numPublishedRecords()); // assert(0 == to3.numPublishedRecords()); //.. // This will produce the following output on 'stdout': //.. // Test Observer ID 1 publishing record number 1 // Context: cause = PASSTHROUGH // count = 1 of an expected 1 total records. //.. // Note that 'to2' produces no output, although its 'publish' method executed // (as verified by the 'numPublishedRecords' method); only 'to1' produces a // printed output, because it has been set to verbose mode. #include <balscm_version.h> #include <ball_context.h> #include <ball_observer.h> #include <ball_record.h> #include <bslma_allocator.h> #include <bslma_usesbslmaallocator.h> #include <bslmf_nestedtraitdeclaration.h> #include <bslmt_lockguard.h> #include <bslmt_mutex.h> #include <bsls_assert.h> #include <bsls_atomic.h> #include <bsls_atomicoperations.h> #include <bsls_review.h> #include <bsl_iosfwd.h> #include <bsl_memory.h> namespace BloombergLP { namespace ball { // ================== // class TestObserver // ================== class TestObserver : public Observer { // This class provides an instrumented implementation of the 'Observer' // protocol suitable for testing systems that use 'Observer'. Each // instance receives a unique (per process) integer identifier at // construction, and keeps count of the number of records that it has // published, as well as the contents of the most recently published record // and context. // // By default, the 'publish' method prints no diagnostic information to the // 'bsl::ostream' supplied at construction. This diagnostic information // can be enabled by a call to 'setVerbose' with a non-zero argument. A // subsequent call to 'setVerbose' with a zero argument will restore the // default behavior of 'publish'. // TYPES typedef bsls::AtomicOperations AtomicOps; typedef bsls::AtomicOperations::AtomicTypes::Int AtomicInt; // CLASS DATA static AtomicInt s_count; // number of instances created // DATA bsl::ostream *d_stream_p; // target of 'publish' method // diagnostic information Record d_record; // most-recently-published record Context d_context; // most-recently-published context int d_id; // unique (per process) id int d_verboseFlag; // "verbosity" mode on 'publish' int d_numRecords; // total number of published records int d_numReleases; // total number of calls to // 'releaseRecords' mutable bslmt::Mutex d_mutex; // serializes concurrent calls to // 'publish' and protects concurrent // access to other class members // NOT IMPLEMENTED TestObserver(const TestObserver&); TestObserver& operator=(const TestObserver&); public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(TestObserver, bslma::UsesBslmaAllocator); // CLASS METHODS static int numInstances(); // Return the total number of instances of this class that have been // created since this process has begun. // CREATORS explicit TestObserver(bsl::ostream *stream, bslma::Allocator *basicAllocator = 0); // Create a test observer having a unique integer identifier, whose // 'publish' method will print diagnostic information (if any) to the // specified 'stream'. Optionally specify a 'basicAllocator' used to // supply memory. If 'basicAllocator' is 0, the currently installed // default allocator is used. By default, this observer prints nothing // to 'stream'. Note that the 'setVerbose' method can affect this // default behavior. #ifndef BDE_OMIT_INTERNAL_DEPRECATED explicit TestObserver(bsl::ostream& stream, bslma::Allocator *basicAllocator = 0); // Create a test observer having a unique integer identifier, whose // 'publish' method will print diagnostic information (if any) to the // specified 'stream'. Optionally specify a 'basicAllocator' used to // supply memory. If 'basicAllocator' is 0, the currently installed // default allocator is used. By default, this observer prints nothing // to 'stream'. Note that the 'setVerbose' method can affect this // default behavior. // // !DEPRECATED!: Use the constructor taking 'bsl::ostream *' instead. #endif // BDE_OMIT_INTERNAL_DEPRECATED virtual ~TestObserver(); // Destroy this test observer. // MANIPULATORS virtual void publish(const Record& record, const Context& context); // Store as the most recently published data the specified log 'record' // and publishing 'context'. If this test observer is in verbose mode, // print an appropriate diagnostic message to the stream specified at // construction. Note that at construction test observers are not in // verbose mode, but that the 'setVerbose' method can affect this mode, // and thus the behavior of this method. // // !DEPRECATED!: Use the alternative 'publish' overload instead. virtual void publish(const bsl::shared_ptr<const Record>& record, const Context& context); // Store as the most recently published data the specified log 'record' // and publishing 'context'. If this test observer is in verbose mode, // print an appropriate diagnostic message to the stream specified at // construction. Note that at construction test observers are not in // verbose mode, but that the 'setVerbose' method can affect this mode, // and thus the behavior of this method. virtual void releaseRecords(); // Discard any shared reference to a 'Record' object that was supplied // to the 'publish' method, and is held by this observer. Note that // this operation should be called if resources underlying the // previously provided shared-pointers must be released. void setVerbose(int flagValue); // Set the internal verbose mode of this test observer to the specified // (boolean) 'flagValue'. The default mode is *not* verbose. If // 'flagValue' is non-zero, calls to 'publish' will print appropriate // diagnostics to the 'bsl::ostream' associated with this test // observer. This printing is suppressed if 'flagValue' is 0. // ACCESSORS int id() const; // Return the unique (per process) integer identifier of this test // observer. const Context& lastPublishedContext() const; // Return a reference to the context most recently published by this // test observer. The behavior is undefined unless 'publish' has been // called at least once, and no other thread is manipulating this // object concurrently (i.e., this function is *not* thread safe). const Record& lastPublishedRecord() const; // Return a reference to the record most recently published by this // test observer. The behavior is undefined unless 'publish' has been // called at least once, and no other thread is manipulating this // object concurrently (i.e., this function is *not* thread safe). int numPublishedRecords() const; // Return the total number of records that this test observer has // published. int numReleases() const; // Return the total number of times that 'releaseRecords' has been // called on this test observer. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ------------------ // class TestObserver // ------------------ // CLASS METHODS inline int TestObserver::numInstances() { return AtomicOps::getIntRelaxed(&s_count); } // CREATORS inline TestObserver::TestObserver(bsl::ostream *stream, bslma::Allocator *basicAllocator) : d_stream_p(stream) , d_record(basicAllocator) , d_context(basicAllocator) , d_id(AtomicOps::incrementIntNvAcqRel(&s_count)) , d_verboseFlag(0) , d_numRecords(0) , d_numReleases(0) { BSLS_ASSERT(d_stream_p); } #ifndef BDE_OMIT_INTERNAL_DEPRECATED inline TestObserver::TestObserver(bsl::ostream& stream, bslma::Allocator *basicAllocator) : d_stream_p(&stream) , d_record(basicAllocator) , d_context(basicAllocator) , d_id(AtomicOps::incrementIntNvAcqRel(&s_count)) , d_verboseFlag(0) , d_numRecords(0) , d_numReleases(0) { } #endif // BDE_OMIT_INTERNAL_DEPRECATED // MANIPULATORS inline void TestObserver::releaseRecords() { bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex); ++d_numReleases; } inline void TestObserver::setVerbose(int flagValue) { bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex); d_verboseFlag = flagValue; } // ACCESSORS inline int TestObserver::id() const { bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex); return d_id; } inline const Context& TestObserver::lastPublishedContext() const { bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex); return d_context; } inline const Record& TestObserver::lastPublishedRecord() const { bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex); return d_record; } inline int TestObserver::numPublishedRecords() const { bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex); return d_numRecords; } inline int TestObserver::numReleases() const { bslmt::LockGuard<bslmt::Mutex> guard(&d_mutex); return d_numReleases; } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------