// ball_loggermanagerconfiguration.t.cpp                              -*-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.
// ----------------------------------------------------------------------------


#include <ball_loggermanagerconfiguration.h>

#include <ball_userfields.h>                                // for testing only

#include <bslim_testutil.h>
#include <bsls_assert.h>

#include <bsl_iostream.h>
#include <bsl_sstream.h>
#include <bsl_string.h>

#include <bsl_cstdlib.h>     // atoi()
#include <bsl_cstring.h>     // strlen()
#include <bsl_functional.h>


using namespace BloombergLP;
using namespace bsl;  // automatically added by script

//=============================================================================
//                                 TEST PLAN
//-----------------------------------------------------------------------------
//                                 Overview
//                                 --------
// This test plan follows the standard approach for components implementing
//
// Abbreviations
// -------------
// For the sake of brevity in the Test Plan below, the following abbreviations
// are used as needed:
//..
//    ball::LMC   ball::LoggerManagerConfiguration
//    ball::LMD   ball::LoggerManagerDefaults
//    Populator   bsl::function<void(UserFields *)>
//    CNF         bsl::function<void(bsl::string *, const char *)>
//    DTC         bsl::function<void(int *, int *, int *, int *, const char*)>
//..
//-----------------------------------------------------------------------------
// [ 1] ball::LoggerManagerConfiguration();
// [ 1] ball::LoggerManagerConfiguration(const LoggerManagerConfiguration&)
// [ 1] ~ball::LoggerManagerConfiguration();
//
// [ 1] ball::LMC& operator=(const ball::LoggerManagerConfiguration& rhs);
// [ 1] void setDefaultValues(const ball::LMD& defaults);
// [ 5] void setLogOrder(LogOrder value);
// [ 6] void setTriggerMarkers(TriggerMarkers value);
// [ 1] void setUserFieldsPopulatorCallback(const Populator&);
// [ 1] void setCategoryNameFilterCallback(const CNF& nameFilter);
// [ 1] void setDefaultThresholdLevelsCallback(const DTC& );
// [ 2] void setDefaultThresholdLevelsIfValid(int)
// [ 1] const ball::LMD& defaults() const;
// [ 5] const LogOrder logOrder() const;
// [ 6] const TriggerMarkers triggerMarkers() const;
// [ 1] const Populator& userFieldsPopulatorCallback() const;
// [ 1] const CNF& categoryNameFilterCallback() const;
// [ 1] const DTC& defaultThresholdLevelsCallback() const;
// [ 1] bsl::ostream& print(bsl::ostream& stream, int level, int spacesPerLev)
//
// [ 1] bool operator==(const ball::LMC& lhs, const ball::LMC& rhs);
// [ 1] bool operator!=(const ball::LMC& lhs, const ball::LMC& rhs);
// [ 1] bsl::ostream& operator<<(bsl::ostream&, const ball::LMC);
//-----------------------------------------------------------------------------
// [ 7] USAGE EXAMPLE
//-----------------------------------------------------------------------------

// ============================================================================
//                     STANDARD BDE ASSERT TEST FUNCTION
// ----------------------------------------------------------------------------

namespace {

int testStatus = 0;

void aSsErT(bool condition, const char *message, int line)
{
    if (condition) {
        cout << "Error " __FILE__ "(" << line << "): " << message
             << "    (failed)" << endl;

        if (0 <= testStatus && testStatus <= 100) {
            ++testStatus;
        }
    }
}

}  // close unnamed namespace

// ============================================================================
//               STANDARD BDE TEST DRIVER MACRO ABBREVIATIONS
// ----------------------------------------------------------------------------

#define ASSERT       BSLIM_TESTUTIL_ASSERT
#define ASSERTV      BSLIM_TESTUTIL_ASSERTV

#define LOOP_ASSERT  BSLIM_TESTUTIL_LOOP_ASSERT
#define LOOP0_ASSERT BSLIM_TESTUTIL_LOOP0_ASSERT
#define LOOP1_ASSERT BSLIM_TESTUTIL_LOOP1_ASSERT
#define LOOP2_ASSERT BSLIM_TESTUTIL_LOOP2_ASSERT
#define LOOP3_ASSERT BSLIM_TESTUTIL_LOOP3_ASSERT
#define LOOP4_ASSERT BSLIM_TESTUTIL_LOOP4_ASSERT
#define LOOP5_ASSERT BSLIM_TESTUTIL_LOOP5_ASSERT
#define LOOP6_ASSERT BSLIM_TESTUTIL_LOOP6_ASSERT

#define Q            BSLIM_TESTUTIL_Q   // Quote identifier literally.
#define P            BSLIM_TESTUTIL_P   // Print identifier and value.
#define P_           BSLIM_TESTUTIL_P_  // P(X) without '\n'.
#define T_           BSLIM_TESTUTIL_T_  // Print a tab (w/o newline).
#define L_           BSLIM_TESTUTIL_L_  // current Line number

// ============================================================================
//                  NEGATIVE-TEST MACRO ABBREVIATIONS
// ----------------------------------------------------------------------------

#define ASSERT_SAFE_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_PASS(EXPR)
#define ASSERT_SAFE_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_FAIL(EXPR)
#define ASSERT_PASS(EXPR)      BSLS_ASSERTTEST_ASSERT_PASS(EXPR)
#define ASSERT_FAIL(EXPR)      BSLS_ASSERTTEST_ASSERT_FAIL(EXPR)
#define ASSERT_OPT_PASS(EXPR)  BSLS_ASSERTTEST_ASSERT_OPT_PASS(EXPR)
#define ASSERT_OPT_FAIL(EXPR)  BSLS_ASSERTTEST_ASSERT_OPT_FAIL(EXPR)

//=============================================================================
//                  GLOBAL TYPEDEFS/CONSTANTS FOR TESTING
//-----------------------------------------------------------------------------

typedef ball::LoggerManagerConfiguration Obj;
typedef ball::LoggerManagerDefaults      Defs;

// Functor typedefs
typedef bsl::function<void(ball::UserFields *)>                      PopCb;
typedef bsl::function<void(bsl::string *, const char *)>             CnfCb;
typedef bsl::function<void(int *, int *, int *, int *, const char*)> DtCb;

//=============================================================================
//                             USAGE EXAMPLE 1
//-----------------------------------------------------------------------------

///Usage
///-----
// The following snippets of code illustrate how to use a
// 'ball::LoggerManagerConfiguration' object.
//
// First, we define a simple function that will serve as a
// 'UserFieldsPopulatorCallback', a callback that will be invoked for each
// logged message to populate user defined fields for the log record:
//..
    void exampleCallback(ball::UserFields *fields)
    {
        fields->appendString("example user field value");
    }
//..
// Next, we define a function 'inititialize' in which we will create and
// configure a 'ball::LoggerManagerConfiguration' object (see
// {'ball_loggermanager'} for an example of how to create the logger-manager
// singleton object):
//..
    void initializeConfiguration(bool verbose)
    {
      ball::LoggerManagerConfiguration config;
//
//..
// Then, we configure the default record buffer size, logger buffer size, and
// the various logging thresholds (see {'ball_loggermanager'} for more
// information on the various threshold levels):
//..
      if (0 != config.setDefaultRecordBufferSizeIfValid(32768) ||
          0 != config.setDefaultLoggerBufferSizeIfValid(1024)  ||
          0 != config.setDefaultThresholdLevelsIfValid(0, 64, 0, 0)) {
         bsl::cerr << "Failed set log configuration defaults." << bsl::endl;
         bsl::exit(-1);
      }
//
      ASSERT(32768 == config.defaultRecordBufferSize());
      ASSERT( 1024 == config.defaultLoggerBufferSize());
      ASSERT(    0 == config.defaultRecordLevel());
      ASSERT(   64 == config.defaultPassLevel());
      ASSERT(    0 == config.defaultTriggerLevel());
      ASSERT(    0 == config.defaultTriggerAllLevel());
//..
// Next, we populate the remaining attributes of our configuration object (note
// that the following methods cannot fail and return 'void'):
//..
      config.setUserFieldsPopulatorCallback(&exampleCallback);
      config.setLogOrder(ball::LoggerManagerConfiguration::e_FIFO);
      config.setTriggerMarkers(ball::LoggerManagerConfiguration::e_NO_MARKERS);
//..
// Now, we verify the options are configured correctly:
//..
      ASSERT(ball::LoggerManagerConfiguration::e_FIFO == config.logOrder());
      ASSERT(ball::LoggerManagerConfiguration::e_NO_MARKERS
                                                   == config.triggerMarkers());
//..
// Finally, we print the configuration value to 'stdout' and return:
//..
      if (verbose) {
        bsl::cout << config << bsl::endl;
      }
    }
//..
// This produces the following (multi-line) output:
//..
//  [
//      Defaults:
//      [
//          recordBufferSize : 32768
//          loggerBufferSize : 1024
//          recordLevel      : 0
//          passLevel        : 64
//          triggerLevel     : 0
//          triggerAllLevel  : 0
//      ]
//      User Fields Populator functor is not null
//      Category Name Filter functor is null
//      Default Threshold Callback functor is null
//      Logging order is FIFO
//      Trigger markers are NO_MARKERS
//  ]
//..

//-----------------------------------------------------------------------------
//          Dummy functions to populate each of the three functors
//-----------------------------------------------------------------------------
void pop(ball::UserFields *)
{
}

void cnf(bsl::string *, const char *)
{
}

void dtc(int *, int *, int *, int *, const char *)
{
}

//-----------------------------------------------------------------------------
//          Some 'ball::LoggerManagerDefaults' field values
//-----------------------------------------------------------------------------

const int NUM_VALUES = 7;

// The 0th element in each array contains the implementation-defined value used
// by the default constructor for 'ball::LoggerManagerDefaults'.

const int _DRBS = Defs::defaultDefaultRecordBufferSize();
const int _DLBS = Defs::defaultDefaultLoggerBufferSize();
const int _DRL  = Defs::defaultDefaultRecordLevel();
const int _DPL  = Defs::defaultDefaultPassLevel();
const int _DTL  = Defs::defaultDefaultTriggerAllLevel();
const int _DTAL = Defs::defaultDefaultTriggerAllLevel();

const int RECORD_BUFFER_SIZE[NUM_VALUES] = { _DRBS, 10, 20, 30, 40, 50, 60 };
const int LOGGER_BUFFER_SIZE[NUM_VALUES] = { _DLBS, 11, 21, 31, 41, 51, 61 };
const int RECORD_LEVEL[NUM_VALUES]       = { _DRL,  12, 22, 32, 42, 52, 62 };
const int PASS_LEVEL[NUM_VALUES]         = { _DPL,  13, 23, 33, 43, 53, 63 };
const int TRIGGER_LEVEL[NUM_VALUES]      = { _DTL,  14, 24, 34, 44, 54, 64 };
const int TRIGGER_ALL_LEVEL[NUM_VALUES]  = { _DTAL, 15, 25, 35, 45, 55, 65 };

//=============================================================================
//                              MAIN PROGRAM
//-----------------------------------------------------------------------------

int main(int argc, char *argv[])
{
    int test = argc > 1 ? atoi(argv[1]) : 0;
    int verbose = argc > 2;
    int veryVerbose = argc > 3;
    //    int veryVeryVerbose = argc > 4; // not used

    cout << "TEST " << __FILE__ << " CASE " << test << endl;;

    //-------------------------------------------------------------------------
    //                           Useful Test Data
    //-------------------------------------------------------------------------

    // Build up a non-default 'ball::LoggerManagerDefaults' value.
    Defs d1;
    d1.setDefaultRecordBufferSizeIfValid(RECORD_BUFFER_SIZE[1]);
    d1.setDefaultLoggerBufferSizeIfValid(LOGGER_BUFFER_SIZE[1]);
    d1.setDefaultThresholdLevelsIfValid(RECORD_LEVEL[1], PASS_LEVEL[1],
                                        TRIGGER_LEVEL[1],TRIGGER_ALL_LEVEL[1]);

    // Populate the three functors (from functions defined above)
    PopCb pCb1(&pop);

    CnfCb cnfCb1(&cnf);

    DtCb dtCb1(&dtc);

    // Create a default ('X0') and a non-default ('X1') attribute object
    const Defs   D0;
    const Defs   D1(d1);

    const PopCb  PCB0;
    const PopCb  PCB1(pCb1);

    const CnfCb  CNFCB0;
    const CnfCb  CNFCB1(cnfCb1);

    const DtCb   DTCB0;
    const DtCb   DTCB1(dtCb1);

    switch (test) { case 0:  // Zero is always the leading case.
      case 7: {
        // --------------------------------------------------------------------
        // TESTING USAGE EXAMPLE
        //   The usage example provided in the component header file must
        //   compile, link, and run on all platforms as shown.
        //
        // Plan:
        //   Incorporate usage example from header into driver, remove leading
        //   comment characters, and replace 'assert' with 'ASSERT'.
        //
        // Testing:
        //   USAGE EXAMPLE
        // --------------------------------------------------------------------

        if (verbose) cout << "\nTesting Usage Example"
                          << "\n=====================" << endl;

        initializeConfiguration(verbose);

      } break;
      case 6: {
        // --------------------------------------------------------------------
        // TESTING  'setTriggerMarkers' AND 'triggerMarkers':
        //   Verify 'setTriggerMarkers' and 'triggerMarkers'.
        //
        // Concern:
        //   That 'setTriggerMarkers' and 'triggerMarkers' works correctly.
        //
        // Plan:
        //   1. Create a logger manager and verify 'logOrder'.
        //   2. Invoke 'setTriggerMarkers' with 'e_NO_MARKERS' and verify
        //      'triggerMarkers'.
        //   3. Invoke 'setTriggerMarkers' with 'e_BEGIN_END_MARKERS' and
        //      verify 'triggerMarkers'.
        //
        // Testing:
        //   void setTriggerMarkers(TriggerMarkers value);
        //   const TriggerMarkers triggerMarkers() const;
        // --------------------------------------------------------------------

        if (verbose)
            cout << "\nTESTING  'setTriggerMarkers' AND 'triggerMarkers'"
                 << "\n=================================================\n";

        Obj lmc;
        ASSERT(lmc.triggerMarkers() == Obj::e_BEGIN_END_MARKERS);

        lmc.setTriggerMarkers(Obj::e_NO_MARKERS);
        ASSERT(lmc.triggerMarkers() == Obj::e_NO_MARKERS);

        lmc.setTriggerMarkers(Obj::e_BEGIN_END_MARKERS);
        ASSERT(lmc.triggerMarkers() == Obj::e_BEGIN_END_MARKERS);

      } break;
      case 5: {
        // --------------------------------------------------------------------
        // TESTING  'setLogOrder' AND 'logOrder':
        //   Verify 'setLogOrder' and 'logOrder'.
        //
        // Concern:
        //   That 'setLogOrder' and 'logOrder' works correctly.
        //
        // Plan:
        //   Create a logger manager and verify 'logOrder'.
        //   Invoke 'setLogOrder' with 'e_FIFO' and verify 'logOrder'.
        //   Invoke 'setLogOrder' with 'e_LIFO' and verify 'logOrder'.
        //
        // Testing:
        //   void setLogOrder(LogOrder value);
        //   const LogOrder logOrder() const;
        // --------------------------------------------------------------------

        if (verbose) cout << "\nTESTING  'setLogOrder' AND 'logOrder'"
                          << "\n====================================" << endl;

        Obj lmc;
        ASSERT(lmc.logOrder() == Obj::e_LIFO);

        lmc.setLogOrder(Obj::e_FIFO);
        ASSERT(lmc.logOrder() == Obj::e_FIFO);

        lmc.setLogOrder(Obj::e_LIFO);
        ASSERT(lmc.logOrder() == Obj::e_LIFO);

      } break;
      case 4: {
        // --------------------------------------------------------------------
        // TESTING 'bdex' STREAMING FUNCTIONALITY:
        //
        // This component does not support 'bdex' streaming.
        // --------------------------------------------------------------------

        if (verbose) cout << "\nTesting Streaming Functionality"
                          << "\n===============================" << endl;

        if (verbose)
            cout << "\nThis component does not support 'bdex' streaming."
                 << endl;

      } break;
      case 3: {
        // --------------------------------------------------------------------
        // TESTING: setDefaultThresholdLevels
        //
        // Concerns:
        //: 1 'setDefaultThresholdLevels' sets the pass-through level to the
        //:    supplied value, and all the other thresholds to 0.
        //:
        //: 2 'setDefaultThresholdLevels' returns 0 on success
        //:
        //: 3 If the supplied pass-through level is not in the ranage [0..255]
        //:   the 'setDefaultThresholdLevels' returns a non-zero value
        //
        // Plan:
        //: 1 Manually call method with valid values and verify the expected
        //:   configuration and the return value is 0.  (C-1, C-2)
        //:
        //: 2 Manually call method with invalid values and verify the
        //:   configuration is not changed and the method is not 0.  (C-3)
        //
        // Testing:
        //   int setDefaultThresholdLevels(int);
        // --------------------------------------------------------------------

        if (verbose) cout
            << endl
            << "TESTING: setDefaultThresholdLevelsIfValid" << endl
            << "=========================================" << endl;


        if (verbose) cout
            << "\nTest successful call to 'setDefaultThresholdLevelsIfValid'."
            << endl;
        {
            Obj mX; const Obj& X = mX;
            ASSERT(0 == mX.setDefaultThresholdLevelsIfValid(5));
            ASSERT(0 == X.defaultRecordLevel());
            ASSERT(5 == X.defaultPassLevel());
            ASSERT(0 == X.defaultTriggerLevel());
            ASSERT(0 == X.defaultTriggerAllLevel());

            ASSERT(0   == mX.setDefaultThresholdLevelsIfValid(100));
            ASSERT(0   == X.defaultRecordLevel());
            ASSERT(100 == X.defaultPassLevel());
            ASSERT(0   == X.defaultTriggerLevel());
            ASSERT(0   == X.defaultTriggerAllLevel());
        }
        if (verbose) cout
            << "\nTest incorrect call to 'setDefaultThresholdLevelsIfValid'."
            << endl;

        {
            Obj mX; const Obj& X = mX;

            ASSERT(0 != mX.setDefaultThresholdLevelsIfValid(-1));
            ASSERT(     RECORD_LEVEL[0] == X.defaultRecordLevel());
            ASSERT(       PASS_LEVEL[0] == X.defaultPassLevel());
            ASSERT(    TRIGGER_LEVEL[0] == X.defaultTriggerLevel());
            ASSERT(TRIGGER_ALL_LEVEL[0] == X.defaultTriggerAllLevel());

            ASSERT(0 != mX.setDefaultThresholdLevelsIfValid(256));
            ASSERT(     RECORD_LEVEL[0] == X.defaultRecordLevel());
            ASSERT(       PASS_LEVEL[0] == X.defaultPassLevel());
            ASSERT(    TRIGGER_LEVEL[0] == X.defaultTriggerLevel());
            ASSERT(TRIGGER_ALL_LEVEL[0] == X.defaultTriggerAllLevel());

        }
      } break;
      case 2: {
        // --------------------------------------------------------------------
        // Constraints Test:
        //
        // Concerns:
        //   This component imposes exactly the constraints on its contained
        //   'ball::LoggerManagerDefaults' object as that object imposes on
        //   itself; the constrained 'set' manipulators and "get" accessors
        //   forward their arguments to the corresponding 'Defaults' methods.
        //   It is sufficient to repeat the test from the 'Defaults' test
        //   driver
        //
        //
        // Plan:
        //    1. Set a "good" function in 'assertH' and 'assertCpp'
        //
        //
        //
        // Testing:
        //    bool isValidDefaultRecordBufferSize(int numBytes);
        //    bool isValidDefaultLoggerBufferSize(int numBytes);
        //    bool areValidDefaultThresholdLevels(int, int, int, int);
        //    int setDefaultRecordBufferSizeIfValid(int numBytes);
        //    int setDefaultLoggerBufferSizeIfValid(int numBytes);
        //    int setDefaultThresholdLevels(int, int, int, int);
        //    int defaultRecordBufferSize();
        //    int defaultLoggerBufferSize();
        //    int defaultRecordLevel();
        //    int defaultPassLevel();
        //    int defaultTriggerLevel();
        //    int defaultTriggerAllLevel();
        // --------------------------------------------------------------------

        if (verbose) cout << endl
                          << "Constraints Test" << endl
                          << "================" << endl;

        if (verbose)
            cout << "\nTesting constraints on contained 'Defaults' object."
                 << endl;
        Obj mX1; const Obj& X1 = mX1;

        if (verbose)
            cout << "\t Check defaultRecordBufferSize contraint. " << endl;
        {
            ASSERT(0 == Obj::isValidDefaultRecordBufferSize(0));
            ASSERT(1 == Obj::isValidDefaultRecordBufferSize(1));
            ASSERT(1 == Obj::isValidDefaultRecordBufferSize(2));

            ASSERT(0 != mX1.setDefaultRecordBufferSizeIfValid(0));
            ASSERT(RECORD_BUFFER_SIZE[0] == X1.defaultRecordBufferSize());
            ASSERT(0 == mX1.setDefaultRecordBufferSizeIfValid(1));
            ASSERT(1 == X1.defaultRecordBufferSize());
            ASSERT(0 == mX1.setDefaultRecordBufferSizeIfValid(2));
            ASSERT(2 == X1.defaultRecordBufferSize());
        }
        if (verbose)
            cout << "\t Check defaultLoggerBufferSize contraint. " << endl;
        {
            ASSERT(0 == Obj::isValidDefaultLoggerBufferSize(0));
            ASSERT(1 == Obj::isValidDefaultLoggerBufferSize(1));
            ASSERT(1 == Obj::isValidDefaultLoggerBufferSize(2));

            ASSERT(0 != mX1.setDefaultLoggerBufferSizeIfValid(0));
            ASSERT(LOGGER_BUFFER_SIZE[0] == X1.defaultLoggerBufferSize());
            ASSERT(0 == mX1.setDefaultLoggerBufferSizeIfValid(1));
            ASSERT(1 == X1.defaultLoggerBufferSize());
            ASSERT(0 == mX1.setDefaultLoggerBufferSizeIfValid(2));
            ASSERT(2 == X1.defaultLoggerBufferSize());
        }
        if (verbose)
            cout << "\t Check defaultThresholdLevels contraint. " << endl;
        {
            ASSERT(0 == Obj::areValidDefaultThresholdLevels(-1, 2, 3, 4));
            ASSERT(0 == Obj::areValidDefaultThresholdLevels(1, -1, 3, 4));
            ASSERT(0 == Obj::areValidDefaultThresholdLevels(1, 2, -1, 4));
            ASSERT(0 == Obj::areValidDefaultThresholdLevels(1, 2, 3, -1));
            ASSERT(0 == Obj::areValidDefaultThresholdLevels(256, 2, 3, 4));
            ASSERT(0 == Obj::areValidDefaultThresholdLevels(1, 256, 3, 4));
            ASSERT(0 == Obj::areValidDefaultThresholdLevels(1, 2, 256, 4));
            ASSERT(0 == Obj::areValidDefaultThresholdLevels(1, 2, 3, 256));
            ASSERT(1 == Obj::areValidDefaultThresholdLevels(1, 2, 3, 4));

            ASSERT(0 != mX1.setDefaultThresholdLevelsIfValid(-1, 2, 3, 4));
            ASSERT(     RECORD_LEVEL[0] == X1.defaultRecordLevel());
            ASSERT(       PASS_LEVEL[0] == X1.defaultPassLevel());
            ASSERT(    TRIGGER_LEVEL[0] == X1.defaultTriggerLevel());
            ASSERT(TRIGGER_ALL_LEVEL[0] == X1.defaultTriggerAllLevel());

            ASSERT(0 != mX1.setDefaultThresholdLevelsIfValid(1, -1, 3, 4));
            ASSERT(     RECORD_LEVEL[0] == X1.defaultRecordLevel());
            ASSERT(       PASS_LEVEL[0] == X1.defaultPassLevel());
            ASSERT(    TRIGGER_LEVEL[0] == X1.defaultTriggerLevel());
            ASSERT(TRIGGER_ALL_LEVEL[0] == X1.defaultTriggerAllLevel());

            ASSERT(0 != mX1.setDefaultThresholdLevelsIfValid(1, 2, -1, 4));
            ASSERT(     RECORD_LEVEL[0] == X1.defaultRecordLevel());
            ASSERT(       PASS_LEVEL[0] == X1.defaultPassLevel());
            ASSERT(    TRIGGER_LEVEL[0] == X1.defaultTriggerLevel());
            ASSERT(TRIGGER_ALL_LEVEL[0] == X1.defaultTriggerAllLevel());

            ASSERT(0 != mX1.setDefaultThresholdLevelsIfValid(1, 2, 3, -1));
            ASSERT(     RECORD_LEVEL[0] == X1.defaultRecordLevel());
            ASSERT(       PASS_LEVEL[0] == X1.defaultPassLevel());
            ASSERT(    TRIGGER_LEVEL[0] == X1.defaultTriggerLevel());
            ASSERT(TRIGGER_ALL_LEVEL[0] == X1.defaultTriggerAllLevel());

            ASSERT(0 != mX1.setDefaultThresholdLevelsIfValid(256, 2, 3, 4));
            ASSERT(     RECORD_LEVEL[0] == X1.defaultRecordLevel());
            ASSERT(       PASS_LEVEL[0] == X1.defaultPassLevel());
            ASSERT(    TRIGGER_LEVEL[0] == X1.defaultTriggerLevel());
            ASSERT(TRIGGER_ALL_LEVEL[0] == X1.defaultTriggerAllLevel());

            ASSERT(0 != mX1.setDefaultThresholdLevelsIfValid(1, 256, 3, 4));
            ASSERT(     RECORD_LEVEL[0] == X1.defaultRecordLevel());
            ASSERT(       PASS_LEVEL[0] == X1.defaultPassLevel());
            ASSERT(    TRIGGER_LEVEL[0] == X1.defaultTriggerLevel());
            ASSERT(TRIGGER_ALL_LEVEL[0] == X1.defaultTriggerAllLevel());

            ASSERT(0 != mX1.setDefaultThresholdLevelsIfValid(1, 2, 256, 4));
            ASSERT(     RECORD_LEVEL[0] == X1.defaultRecordLevel());
            ASSERT(       PASS_LEVEL[0] == X1.defaultPassLevel());
            ASSERT(    TRIGGER_LEVEL[0] == X1.defaultTriggerLevel());
            ASSERT(TRIGGER_ALL_LEVEL[0] == X1.defaultTriggerAllLevel());

            ASSERT(0 != mX1.setDefaultThresholdLevelsIfValid(1, 2, 3, 256));
            ASSERT(     RECORD_LEVEL[0] == X1.defaultRecordLevel());
            ASSERT(       PASS_LEVEL[0] == X1.defaultPassLevel());
            ASSERT(    TRIGGER_LEVEL[0] == X1.defaultTriggerLevel());
            ASSERT(TRIGGER_ALL_LEVEL[0] == X1.defaultTriggerAllLevel());

            ASSERT(0 == mX1.setDefaultThresholdLevelsIfValid(1, 2, 3, 4));
            ASSERT(1 == X1.defaultRecordLevel());
            ASSERT(2 == X1.defaultPassLevel());
            ASSERT(3 == X1.defaultTriggerLevel());
            ASSERT(4 == X1.defaultTriggerAllLevel());
        }
      } break;
      case 1: {
        // --------------------------------------------------------------------
        // Basic Attribute Test:
        // --------------------------------------------------------------------

        if (verbose) cout << endl
                          << "Basic Attribute Test" << endl
                          << "=====================" << endl;

        Obj mX1;  const Obj& X1 = mX1;
        Obj mY1;  const Obj& Y1 = mY1;
        Obj mZ1;  const Obj& Z1 = mZ1; // Z1 is the control
        if (verbose) { cout << "X1 = ";  X1.print(cout, -1, 4); }

        if (verbose) cout << "\nCheck default ctor. " << endl;

        ASSERT(D0 == X1.defaults());

        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(1 == (X1 == Z1));          ASSERT(0 == (X1 != Z1));
        ASSERT(1 == (Z1 == Y1));          ASSERT(0 == (Z1 != Y1));
        ASSERT(1 == (Y1 == Z1));          ASSERT(0 == (Y1 != Z1));

        if (verbose) cout << "\tSetting default values explicitly." <<endl;

        mX1.setDefaultValues(D0);
        mX1.setUserFieldsPopulatorCallback(PCB0);
        mX1.setCategoryNameFilterCallback(CNFCB0);
        mX1.setDefaultThresholdLevelsCallback(DTCB0);

        ASSERT(D0 == X1.defaults());

        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(1 == (X1 == Z1));          ASSERT(0 == (X1 != Z1));
        ASSERT(1 == (Z1 == Y1));          ASSERT(0 == (Z1 != Y1));
        ASSERT(1 == (Y1 == Z1));          ASSERT(0 == (Y1 != Z1));

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        if (verbose) cout << "\tTesting set/get methods."
                          << "\n\t\tChange attribute 0." << endl;

        mX1.setDefaultValues(D1);

        if (veryVerbose) { cout << "\t\tX1 = ";  X1.print(cout, -5, 4); }

        ASSERT(D1 == X1.defaults());
        ASSERT(!X1.userFieldsPopulatorCallback());
        ASSERT(!X1.categoryNameFilterCallback());
        ASSERT(!X1.defaultThresholdLevelsCallback());

        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(0 == (X1 == Z1));          ASSERT(1 == (X1 != Z1));
        ASSERT(0 == (Z1 == X1));          ASSERT(1 == (Z1 != X1));
        ASSERT(1 == (Y1 == Z1));          ASSERT(0 == (Y1 != Z1));
        {
            Obj C(X1);
            ASSERT(1 == (C == X1));       ASSERT(0 == (C != X1));
        }

        mY1 = X1;
        ASSERT(1 == (Y1 == Y1));          ASSERT(0 == (Y1 != Y1));
        ASSERT(1 == (Y1 == X1));          ASSERT(0 == (Y1 != X1));
        ASSERT(0 == (Y1 == Z1));          ASSERT(1 == (Y1 != Z1));

        mX1.setDefaultValues(D0);
        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(1 == (X1 == Z1));          ASSERT(0 == (X1 != Z1));
        ASSERT(0 == (Y1 == Z1));          ASSERT(1 == (Y1 != Z1));

        mX1 = mY1 = Z1;
        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(1 == (X1 == Z1));          ASSERT(0 == (X1 != Z1));
        ASSERT(1 == (Y1 == Z1));          ASSERT(0 == (Y1 != Z1));

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        if (verbose) cout << "\t\tChange attribute 1." << endl;

        mX1.setUserFieldsPopulatorCallback(PCB1);

        if (veryVerbose) { cout << "\t\tX1 = ";  X1.print(cout, -5, 4); }

        ASSERT(D0 == X1.defaults());

        ASSERT(X1.userFieldsPopulatorCallback());
        ASSERT(!X1.categoryNameFilterCallback());
        ASSERT(!X1.defaultThresholdLevelsCallback());

        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(0 == (X1 == Z1));          ASSERT(1 == (X1 != Z1));
        ASSERT(0 == (Z1 == X1));          ASSERT(1 == (Z1 != X1));
        ASSERT(1 == (Y1 == Z1));          ASSERT(0 == (Y1 != Z1));
        {
            Obj C(X1);
            ASSERT(1 == (C == X1));       ASSERT(0 == (C != X1));
        }

        mY1 = X1;
        ASSERT(1 == (Y1 == Y1));          ASSERT(0 == (Y1 != Y1));
        ASSERT(1 == (Y1 == X1));          ASSERT(0 == (Y1 != X1));
        ASSERT(0 == (Y1 == Z1));          ASSERT(1 == (Y1 != Z1));

        mX1.setUserFieldsPopulatorCallback(PCB0);

        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(1 == (X1 == Z1));          ASSERT(0 == (X1 != Z1));
        ASSERT(0 == (Y1 == Z1));          ASSERT(1 == (Y1 != Z1));

        mX1 = mY1 = Z1;
        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(1 == (X1 == Z1));          ASSERT(0 == (X1 != Z1));
        ASSERT(1 == (Y1 == Z1));          ASSERT(0 == (Y1 != Z1));

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        if (verbose) cout << "\t\tChange attribute 2." << endl;

        mX1.setCategoryNameFilterCallback(CNFCB1);

        if (veryVerbose) { cout << "\t\tX1 = ";  X1.print(cout, -5, 4); }

        ASSERT(D0 == X1.defaults());

        ASSERT(!X1.userFieldsPopulatorCallback());
        ASSERT(X1.categoryNameFilterCallback());
        ASSERT(!X1.defaultThresholdLevelsCallback());

        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(0 == (X1 == Z1));          ASSERT(1 == (X1 != Z1));
        ASSERT(0 == (Z1 == X1));          ASSERT(1 == (Z1 != X1));
        ASSERT(1 == (Y1 == Z1));          ASSERT(0 == (Y1 != Z1));
        {
            Obj C(X1);
            ASSERT(1 == (C == X1));       ASSERT(0 == (C != X1));
        }

        mY1 = X1;
        ASSERT(1 == (Y1 == Y1));          ASSERT(0 == (Y1 != Y1));
        ASSERT(1 == (Y1 == X1));          ASSERT(0 == (Y1 != X1));
        ASSERT(0 == (Y1 == Z1));          ASSERT(1 == (Y1 != Z1));

        mX1.setCategoryNameFilterCallback(CNFCB0);

        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(1 == (X1 == Z1));          ASSERT(0 == (X1 != Z1));
        ASSERT(0 == (Y1 == Z1));          ASSERT(1 == (Y1 != Z1));

        mX1 = mY1 = Z1;
        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(1 == (X1 == Z1));          ASSERT(0 == (X1 != Z1));
        ASSERT(1 == (Y1 == Z1));          ASSERT(0 == (Y1 != Z1));

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        if (verbose) cout << "\t\tChange attribute 3." << endl;

        mX1.setDefaultThresholdLevelsCallback(DTCB1);

        if (veryVerbose) { cout << "\t\tX1 = ";  X1.print(cout, -5, 4); }

        ASSERT(D0 == X1.defaults());

        ASSERT(!X1.userFieldsPopulatorCallback());
        ASSERT(!X1.categoryNameFilterCallback());
        ASSERT(X1.defaultThresholdLevelsCallback());

        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(0 == (X1 == Z1));          ASSERT(1 == (X1 != Z1));
        ASSERT(0 == (Z1 == X1));          ASSERT(1 == (Z1 != X1));
        ASSERT(1 == (Y1 == Z1));          ASSERT(0 == (Y1 != Z1));
        {
            Obj C(X1);
            ASSERT(1 == (C == X1));       ASSERT(0 == (C != X1));
        }

        mY1 = X1;
        ASSERT(1 == (Y1 == Y1));          ASSERT(0 == (Y1 != Y1));
        ASSERT(1 == (Y1 == X1));          ASSERT(0 == (Y1 != X1));
        ASSERT(0 == (Y1 == Z1));          ASSERT(1 == (Y1 != Z1));

        mX1.setDefaultThresholdLevelsCallback(DTCB0);

        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(1 == (X1 == Z1));          ASSERT(0 == (X1 != Z1));
        ASSERT(0 == (Y1 == Z1));          ASSERT(1 == (Y1 != Z1));

        mX1 = mY1 = Z1;
        ASSERT(1 == (X1 == X1));          ASSERT(0 == (X1 != X1));
        ASSERT(1 == (X1 == Z1));          ASSERT(0 == (X1 != Z1));
        ASSERT(1 == (Y1 == Z1));          ASSERT(0 == (Y1 != Z1));

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        if (verbose) cout << "Testing output operator<<." << endl;

        mY1.setDefaultValues(D1);
        mY1.setUserFieldsPopulatorCallback(PCB1);
        mY1.setCategoryNameFilterCallback(CNFCB1);
        mY1.setDefaultThresholdLevelsCallback(DTCB1);

      } break;
      default: {
        cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl;
        testStatus = -1;
      }
    }

    if (testStatus > 0) {
        cerr << "Error, non-zero test status = " << testStatus << "." << endl;
    }
    return testStatus;
}

// ----------------------------------------------------------------------------
// 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 ----------------------------------
