//-*-c++-*--------------------------------------------------------------
//  nodeTest.cpp
//   Tests the node class...
//
//      Unpublished Confidential Information of Hewlett-Packard
//      Company. Do not disclose.  Copyright (c) Hewlett-Packard
//      Company 1995. All rights reserved
// 
// The contents of this software are proprietary and confidential to
// the Hewlett- Packard Company, and are limited in distribution to
// those with a direct need to know.  Individuals having access to
// this software are responsible for maintaining the confidentiality
// of the content and for keeping the software secure when not in use.
// Transfer to any party is strictly forbidden other than as expressly
// permitted in writing by Hewlett-Packard Company.  Unauthorized
// transfer to or possession by any unauthorized party may be a
// criminal offense.
//
//                  RESTRICTED RIGHTS LEGEND
//
//      Use,  duplication,  or disclosure by the U. S. Government is
//      subject to restrictions as set forth in subparagraph (c) (1)
//      (ii) in the Rights in Technical Data and Computer Software
//      clause in DFARS 252.227-7013 or any other successor clause.
//
//                      HEWLETT-PACKARD COMPANY
//                      3000 Hanover St.
//                      Palo Alto, CA  94304 U.S.A.
//
//      Rights for non-DOD U.S. Government Departments and Agencies
//      are as set forth in FAR 52.227-19 (c) (1,2) or any other
//      successor clause.
//
//  Usage: nodeTest [devfilename] [CDSLE]
//
//    C - Continue testing even though earlier tests may have failed.
//    D - Test Node with embedded data.
//    S - Test Node with Short ADs.
//    L - Test Node with Long ADs.
//    E - Test Node with Extended ADs.
//
//  NOTE: You can specify all of these, but turning any on will
//    automatically turn off all bytes not present.
//
//  Assumes:  Partition starts at sector 257.
//  Assumes:  (By default) Test should terminate after first failure.
//  Assumes:  (By default) Embedded data, shortADs, longADs, and
//            extendedADs should be tested.
//  
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfAncil/ssTest/nodeTest.cpp,v $
// $Revision: 1.28 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/04/30 23:32:18 $
//

//-----------------------------------------------------------------------
//                  N O D E S   T E S T   P L A N 
//
// Nodes testing takes part in two phases:
//
// (1) Interface Verification.  (Confirm correct functionality 
//     of the Node class interface.)
//
//     This includes verfication of state changes to a Node
//     object after setting/getting portions of the Node state.
//        
//     This test will guarantee that the Node class correctly
//     provides manipulation functionality of the state entrusted
//     to it.  Simply stated, successful completion of this test
//     verifies the Node class' gross state setting functionality 
//     is correct.  (We can trust that Node::setEmbeddedData() 
//     actually sets that information, and does not interfere with
//     the data set from a setEmbeddedEASpace(), etc.)
// 
//     This test confirms a mere write/read verification at
//     the most trivial level.
//
// (2) Corner Case Verification.  (Confirm the Node class behaves
//     correctly for specific [exceptional] corner cases.)
//
//     This includes exercising Node functionality at specific
//     (predetermined) functional cases, such as all ADs exactly
//     fitting into the written descriptor, and cases where 
//     we have one more AD than could fit into the written descriptor.
//
//     This test will guarantee that the Node class correctly
//     handles reading and writing to the surface with unique
//     internal logic.  This test confirms the Node class will
//     correctly and robustly handle Node processing situations,
//     especially those operations going to disk.
//
//     This test intends to force the exercise of all internal 
//     Node logic, especially that logic written to handle 
//     exceptional Node state conditions in read and write operations.  
//     We verify a Node with internal state violation does not 
//     write itself out.
//
//-------------------------------------------------------------------
// ( 1 )    I N T E R F A C E   V E R I F I C A T I O N
// The following notes are thoughts on getting interface
//   verification.
//
//   o For each externally modifiable member (at least indirectly
//       accessible): dataEL_, reserveEL_, embeddedData_, 
//       embeddedEASpace_, uniqueID_, attributes_:
//
//     - Set state.
//     - Retrieve state.
//     - Confirm retrieved state was the state set.
//     - Change state.
//     - Set state.
//     - Retrieve state.
//     - Confirm retrieved state is the same as set state,
//        but different from originally set state.
//
//   o Perform the above testing sequentially for each modifiable
//     member (complete all test steps for embeddedData_, then
//     for embeddedEASpace_, etc.)
//
//     This will confirm the behavior of the interface functions:
//     that they do, in fact, modify the state of the Node
//     object (and the single member in particular), and that
//     the member's state is correctly stored and retrieved.
//
//   o Perform the above testing in parallel for each modifiable
//     member (set states for all members, then retrieve states
//     for all members.  Then change state for all, and compare
//     for all.)
//
//     This will confirm that each member is able to store its
//     state distinctly and in isolation from all other members.
//     For example, it will confirm setting embeddedData_ does
//     not overwrite the state of embeddedSpaceEA_.
//   
//   o Instantiate a Node with correct internal state and
//     confirm a write operation followed by a read operation
//     returns the correct state.  Change the state of the
//     Node and repeat.
//
//   o Instantiate a Node with incorrect internal state and
//     confirm a write operation fails.  Confirm the surface
//     is not touched for the failed write (that a previous 
//     successful Node written to that location can still
//     be read (verify its state is the same as the state of
//     the node correctly written).
//
//  NOTE: These tests will (indirectly) confirm that the
//     Node::operator==() and Node::operator!=() operators
//     return opposite, and correct, values.
//
//   The following is the verification process:
//
//   o Set state with interface functions
//     (Set state [orig], set new state [mod]
//       test: mod != orig
//     restore state [restore],
//       test: restore == orig, restore != mod
//     - get/set dataEL
//     - get/set reserveEL
//     - get/set embeddedData
//     - get/set uniqueID
//     - get/set infolen
//
//   o Verify internal logic of Node
//     - verify internal size calculations
//     - verify conflict between ADs and embedded data.
//
//   o Verify Node read/write integrity.
//     
//-------------------------------------------------------------------
// ( 2 )    C O R N E R   C A S E   V E R I F I C A T I O N 
// The following notes are thoughts on getting full coverage of
//   Nodes corner cases:
// 
//   o Create nodes of various sizes, with ADs or embedded data,
//     and confirm state integrity or violation.
//
//     THE FOLLOWING ARE ALLOWED CASES (Node::toDisk() would succeed)
//     - There are no ADs.
//     - All ADs do not fit in the descriptor (no or some EA space).
//       . one AD fits.
//       . exactly one AD fits.
//       . multiple ADs fit.
//       . multiple ADs exactly fit.
//     - All ADs fit in the descriptor (no or some EA space).
//     - All ADs exactly fit in the descriptor (no or some EA space).
//     - There is no embedded data (no or some EA space, all EA space
//       exactly fits).
//     - All embedded data fits in the Descriptor (no or some EA space).
//     - All embedded data exactly fits in the descroptor (no or some
//       EA space).
//
//     THE FOLLOWING ARE NOT ALLOWED CASES (Node::toDisk() would fail)
//     - Too much descriptor data is present (data and EAs > available).
//     - Too much descriptor data is present (data only).
//     - Too much descriptor data is present (EAs only).
//     - Too much descriptor data is present (EAs and one AD).
//
//     FOR NODES WITH CORRECT INTERNAL LOGIC:
//     - Write Node to the disk
//       . various disk locations
//       . various Node sizes
//         - overflow ADs to AEDs
//         - all ADs fit in descriptor
//         - all embedded data fits in descroptor
//     - Read Node from the disk (the written location).
//     - Compare, read_Node == written_Node.
//
//     FOR NODES WITH INCORRECT INTERNAL LOGIC:
//     - Attempt a Node write to disk.
//     - Confirm the node write failed (error returned, disk not hit).
// 

#include "nsrLib.h"
#include <stdlib.h>
#include "nsrRSSdv.h"

void
PRINT_MESSAGE(CONST_CHAR_PTR message)
{ // Print this message if it is not NULL.
    if(message)
        printf("%s\n", message);
}
void
PRINT_ERROR(CONST_CHAR_PTR  message)
{ // Print this error (it should not be NULL, but we check
    //   anyway).
    if(message)
        printf("ERROR: %s\n", message);
    else
        printf("ERROR: (something)  HEY!  I need a message!\n");
}

// Print the message as if it were an error message.
void
errorMsg( CONST_CHAR_PTR message) { 
    printf("----->ERROR: %s \n", message); 
}

//  Print the following error parameter (and its message).
void
printError( const Error & errout ) { 
    printf("-->ERROR STATE: %s \n", (CHAR_PTR) errout.message() ); 
}


// Print out the error and the message if the error parameter is in an
//   error state.
void
printIfError( Error & err ) {
    if (err) 
        printError( err );
} // printIfErr


// Status variables for our tests.
enum TestStatus { TEST_SUCCESS, TEST_FAIL };

//------------------------------------
// R O U N D _ U P _ B Y _ F O U R ( )
// Round the number passed in UP to a number with a four byte multiple.
UINT32
ROUND_UP_BY_FOUR(UINT32 num)
{
    num += (num%4) ? 4-(num%4) : (num%4);
    return num;
}

//----------------------------------------
// R O U N D _ D O W N _ B Y _ F O U R ( )
// Round the number passed in DOWN to a number with a four byte multiple.
UINT32
ROUND_DOWN_BY_FOUR(UINT32 num)
{
    num -= (num%4);
    return num;
}

// The following functions are used for testing Nodes.

// The highest level function (do all testing).
UINT32
performNodeTesting(CONST_CHAR_PTR devFileName);

// Do half of testing: the interface.
TestStatus
testNodeInterface(SpaceResource& space, 
                  LogExtent& logExtent);
// Test the Node's usage of surface space.
TestStatus
testNodeSpaceUsage(SpaceResource& space, 
                   LogExtent& logExtent);
// Do half of testing: the corner cases.
TestStatus
testNodeCornerCases(SpaceResource& space, 
                    LogExtent& logExtent);

// Test the Node corner cases for this ADType.
TestStatus
testNodeCornerCaseSuite(SpaceResource& space, 
                        LogExtent& logExtent, 
                        ADType adType);

// Test the Node corner cases for this ADType, and the EA bytes
//   specified.
TestStatus
testNodeADCornerCaseSuite(SpaceResource& space, 
                          LogExtent& logExtent, 
                          ADType adType,
                          UINT32 numEABytes,
                          UINT16 canWriteADsFlag,
                          TestStatus emptyNodeExpectedStatus,
                          TestStatus writeADsExpectedStatus);

// Test the corner cases around the range of ADs for this Node
//   state.
TestStatus
testADRangeCornerCases( SpaceResource& space, 
                        LogExtent& logExtent, 
                        ADType adType,
                        UINT32 numEABytes,
                        UINT32 lowNum, 
                        UINT32 highNum,
                        TestStatus expectedStatus);

// Test the Node corner cases for this ADType, the EA bytes and
//   number of ADs/embedded data specified.
TestStatus
distributeBodyAndTailAndTest(SpaceResource& space, 
                             LogExtent& logExtent, 
                             UINT64 uniqueID, 
                             UINT32 numEABytes, 
                             ADType adType, 
                             UINT32 numItems, 
                             TestStatus  expectedStatus);

// Instantiate the node with the state given, and confirm
//   its (correct) integrity by direct query (Node::canBeWritten())
//   and actual write/read verification.
TestStatus
makeNodeAndTest(SpaceResource& space, 
                LogExtent& logExtent, 
                UINT64 uniqueID, 
                UINT32 numEABytes, 
                ADType type, 
                UINT32 dataLength, 
                UINT32 numBodyAds, 
                UINT32 numTailADs,
                TestStatus  expectedStatus);

// Populate the Node passed in with the state specified.
Error
populateNode(SpaceResource& space, 
             Node& node, 
             UINT64 uniqueID, 
             UINT32 numEABytes, 
             ADType type, 
             UINT32 dataLength, 
             UINT32 numBodyADs, 
             UINT32 numTailADs);

// Return a uniquely populated ByteArray of the length specified.
// NOTE:  We populate each character specifically, which happens
//   to be "A" for the first, "B" for the second... incrementing
//   each value thereafter (past byte overflow).
ByteArray
getByteArray(UINT32 dataLength);

TestStatus
testNodeIntegrity(Node& node, 
                  LogExtent& logExtent, 
                  ADType adType);
TestStatus
testNodeWriteRead(SpaceResource& space, 
                  LogExtent& logExtent, 
                  Node& node, 
                  ADType adType);
LogExtent*
getNextTestExtent(Partition& partition, 
                  DriveDevice& driveDevice, 
                  UINT32 lowBounds, 
                  UINT32 highBounds);
UINT32
getRandomSector(UINT32 low,
                UINT32 high);

void
writeNodeAndReadBack(Node& node, 
                     Node& readBackNode,
                     SpaceResource& space, 
                     LogExtent& ext, 
                     ADType adType, 
                     Error& err);
TestStatus
confirmInterfaceCase(SpaceResource& space, 
                     LogExtent& logExtent, 
                     UINT64 uniqueID, 
                     UINT32 numEABytes,
                     ADType type, 
                     UINT32 dataLength, 
                     UINT32 numBodyADs, 
                     UINT32 numTailADs);
Error
setLogExtentList(SpaceResource& space, 
                 LogExtentList& logExtentList, 
                 UINT32 numADs);

// Write out a node with the number of AEDs passed in.
//   Append all blocks used to the extent list passed in, including
//   the extent for the FileEntry portion of the Node.
Error
writeNodeAndAppendToEL(SpaceResource& space,
                       UINT32 numAEDs,
                       LogExtentList& list);

void
writeZerosToSpaceResource(SpaceResource& space,
                          Error);

TestStatus
confirmWrittenStatus(LogExtentList& list,
                     Boolean expected);

Boolean
extentIsWritten(LogExtent& logExtent,
                Error err);

TestStatus
confirmAvailableBlocksNotWritten(SpaceResource& space);

TestStatus
writeAEDNodeTypes(SpaceResource& space, 
                  LogExtentList& logExtentList);

UINT32
numBytesForAD(ADType adType);

// Return the maximum number of EA bytes we can store in a FileEntry.
UINT32
getMaxEABytes(LogExtent& logExtent,
              ADType adType);

// Return the maximum number of bytes available in a FileEntry.
UINT32
getMaxFileEntryBytes(LogExtent& logExtent,
                     ADType adType);

UINT32
getMaxNumItemsInFileEntry( LogExtent& logExtent, 
                           ADType adType, 
                           UINT32 numEABytes);
UINT32
getMaxNumItemsInAED( LogExtent& logExtent, 
                     ADType adType);

void
checkTest(TestStatus status, 
          TestStatus& returnStatus,
          CONST_CHAR_PTR errorMessage, 
          CONST_CHAR_PTR successMessage = NULL);

// If the global_continue is non-zero, we will continue test
//   processing even though previous tests may have failed.
//   However, if global_continue is zero, we will abort testing
//   at the first failed test.
UINT32 global_continue         = 0;  // Don't continue by default.
UINT32 global_testEmbeddedData = 1;  // Test by default.
UINT32 global_testShortADs     = 1;  // Test by default.
UINT32 global_testLongADs      = 1;  // Test by default.
UINT32 global_testExtendedADs  = 1;  // Test by default.

Partition* global_imaginary_partition;   // Used in setLogExtentList() for
//   imaginary extents.

// This global is the starting block location on the surface where
//   our partition begins.  We begin allocating at the begin of
//   our partition.
const UINT32 ASSUMED_PART_START = 257; // the partition starts sector #

// Just confirm we got a parameter for the device name.
Boolean
paramsOk(UINT16 argc, CHAR_PTR *argv);

UINT32
main (INT16 argc, CHAR_PTR argv[])
{
    INT32 returnVal;

    if(! paramsOk(argc, argv)) 
    { // If the parameters are not acceptable, abort.
        returnVal = -1;  // Bad input parameters.
    }
    else
    { // We have acceptable input parameters.
        // RECALL:  argv[1] is the target device.
        if(returnVal = performNodeTesting(argv[1]))
        { // If errors were encountered.
            printf(" >>>>>>>>    ERRORS ENCOUNTERED DURING TEST (%ld returned).   <<<<<<<<< \n\n", returnVal);
        }
#ifndef NO_SILLY_GRATIFICATION_CLAUSE_TO_ENHANCE_SELF_ESTEEM
        else
        { // No errors.
            printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
            printf("+++++++++++++++++   WAY TO GO, CHARLEY!   ++++++++++++++++++++\n");
            printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
        }
#endif
    }
    return returnVal;
}

//--------------------
// p a r a m s O k ( )
// Check the argc and argv arguments from the command line, and
//   return TRUE if they are ok (argv[1] is the device).
Boolean
paramsOk(UINT16 argc, CHAR_PTR *argv)
{
    if  ( (argc == 1)  
          || (strcmp(argv[1], "-?") == 0) 
          || (strcmp(argv[1], "-h") == 0) 
          || (strcmp(argv[1], "-H") == 0) 
          || (strcmp(argv[1], "/?") == 0) 
          || (strcmp(argv[1], "/h") == 0) 
          || (strcmp(argv[1], "/H") == 0) 
          || (argc > 3) 
        ) {
        printf( "Usage: nodeTest [devfilename] [CDSLE]\n");
        printf( "    C - Continue testing even though earlier tests may have failed.\n");
        printf( "    D - Test Node with embedded data.\n");
        printf( "    S - Test Node with Short ADs.\n");
        printf( "    L - Test Node with Long ADs.\n");
        printf( "    E - Test Node with Extended ADs.\n");
        printf( "  NOTE: You can specify all of these, but turning any on will\n");
        printf( "    automatically turn off all bytes not present.\n\n");
        printf( "  Assumes:  Partition starts at sector 257.\n");
        printf( "  Assumes:  (By default) Test should terminate after first failure.\n");
        printf( "  Assumes:  (By default) Embedded data, shortADs, longADs, and\n");
        printf( "            extended ADs should be tested.\n");
        return False;
    }
    else if(argc == 3)
    {  // We assume the second parameter is our flags.
        // Turn everything off.
        global_testEmbeddedData = 0;
        global_testShortADs     = 0;
        global_testLongADs      = 0;
        global_testExtendedADs  = 0;

        if(strchr(argv[2], 'C') || strchr(argv[2], 'c'))
            global_continue = 1;  // Continue even if a test fails.
        if(strchr(argv[2], 'D') || strchr(argv[2], 'd'))
            global_testEmbeddedData = 1;  // Test this.
        if(strchr(argv[2], 'S') || strchr(argv[2], 's'))
            global_testShortADs = 1;  // Test this.
        if(strchr(argv[2], 'L') || strchr(argv[2], 'l'))
            global_testLongADs = 1;  // Test this.
        if(strchr(argv[2], 'E') || strchr(argv[2], 'e'))
            global_testExtendedADs = 1;  // Test this.
    }

    // At this point, we assume argv[1] is the device file.
    return True; // Everything is ok.
}

//----------------------------------------
// p e r f o r m N o d e T e s t i n g ( )
// This is the highest level of all nodes testing.  From here we branch to testing
//   the Node interface and the Node corner cases.
// Return zero if all testing completed successfully, or non-zero
//   on error.
//     0   Success.
//     1   We could not give space to the SpaceResource.
//     2   Node interface test failure.
//     3   Node space usage test failure.
//     4   Node corner case test failure.
UINT32
performNodeTesting(CONST_CHAR_PTR devFileName)
{
    UINT32 returnVal = 0;  // Success by default.

    // THE FOLLOWING STATIC DATA IS INSTANTIATED ONLY SO WE CAN INTERACT WITH 
    //   THE SURFACE.
    static DriveDevice driveDevice(devFileName);
    static PhysicalVolume physVolume(&driveDevice);
    static UINT32 startSector = ASSUMED_PART_START;
    static UINT32 numSectors = physVolume.numSectors() - startSector -2;
    static PhysExtent physExtent(&physVolume, startSector, numSectors);
    static Partition partition(physExtent);
    global_imaginary_partition = & partition;
    static LogicalVolume lvol(&partition);

    Error err(error_None);
    
    // Instantiate a SpaceResource and extent for where this node
    //   can be written.  We are using an sr_UnerasedTable because the 
    //   other node test used it.
    static LELSpaceResource space(*partition.logVolumep(),
                                  srSpace_UnerasedOnly);
    if (err) {
#ifdef NODE_DEBUG
        errorMsg("COULD NOT CONSTRUCT LELSpaceResource!");
        printError(err);
#endif
        returnVal = 1;
        return returnVal;
    }

    // Now, give some space to our space resource.
    UINT32 spaceLength = partition.blockSize() *
        (partition.numBlocks() - (startSector+2));
#ifdef NODE_DEBUG
    printf("Giving space an extent of infoLen %ld starting at block %ld.\n", 
           spaceLength, startSector);
#endif

    LogExtent spaceExtent(&partition, startSector, spaceLength);
    if (err=spaceExtent.error()) {
#ifdef NODE_DEBUG
        errorMsg("Could not construct spaceExtent");
        printError(err);
#endif
        returnVal = 1;
        return returnVal;
    }
        
    space.putLogExtent(spaceExtent,err);
    if (err) {
        errorMsg("COULD NOT PUT SPACEEXTENT INTO SPACE");
        printError(err);
        returnVal = 1;
        return returnVal;
    }
    UINT64 availBytes = space.availBytes();

    if(availBytes != spaceLength)
    { // If we were unsuccessful in setting the available space.
        errorMsg("COULD NOT SET SPACE FOR SPACE RESOURCE!");
        returnVal = 1;  // Could not set space in the space resource.
    }
    else
    { // We successfully added space to the space resource.

        // We need to instantiate a LogExtent for all the supporting functions
        //   to refer to.  This is logically the extent for the FileEntry
        //   portion of the Node, and this extent will be queried by other 
        //   test functions for blockSize().
        LogExtent* logExtent = getNextTestExtent(partition, 
                                                 driveDevice, 
                                                 startSector, 
                                                 startSector+numSectors);

        // Test the node interface functions.
        TestStatus status;
  
        // We need to test the node interface.  This is a confirmation
        //   that needs to be done first, but only once.  A successful
        //   running of the interface tests will confirm that we do get
        //   the desired Node state change when modifying interface
        //   functions are called (to set extents or embedded data, etc.)
        //   and that accessor functions correctly get the information
        //   from the entity (the correct embedded data or extents are
        //   returned, etc.)
        status = testNodeInterface(space, *logExtent);
  
        // If the Node interface is functioning properly, we can continue
        //   to test the corner cases of Node behavior with various types
        //   and amounts of EAs, ADs, and embedded data.
        if(status != TEST_SUCCESS)
        {
            // If the node interface tests failed.
            errorMsg("Node interface tests failed.");
            returnVal = 2;  // Error in interface tests.
        }
        else
        { // The node interface functions properly, test the conservation
            //   of space.
// status = testNodeSpaceUsage(space, *logExtent);
            status = TEST_SUCCESS;

            if(status != TEST_SUCCESS)
            { // If we failed in space usage.
                errorMsg("Node space usage tests failed.");
                returnVal = 3;  // Error in corner case tests.
            }
            else
            { // The node interface functions properly, test the node corner
                //   cases.
                // Begin our testing of Nodes.
                status = testNodeCornerCases(space, *logExtent);  // Pass in the device file name.
    
                if(status != TEST_SUCCESS)
                { // If we failed in the node corner cases.
                    errorMsg("Node corner-case tests failed.");
                    returnVal = 4;  // Error in corner case tests.
                }
            }
        }
    }
    return returnVal;
}

//--------------------------------------
// t e s t N o d e I n t e r f a c e ( )
// This function confirms the basic Nodes interface performs as expected:
//   setting internal data causes state changes, that data can later be
//   retrieved, etc.
TestStatus
testNodeInterface(SpaceResource& space,
                  LogExtent& logExtent)
{
    // To be added in the future.
    return TEST_SUCCESS;
}

//--------------------------------
// t e s t S p a c e U s a g e ( )
// This function confirms the Node class only uses space on the surface
//   that is allocated to it, and that it frees surface space it no
//   longer needs.  Thus, this test confirms the conservation of space
//   (no space leakage), and that the Node does not corrupt other parts
//   of the surface on writes/reads.
// TEST PLAN:
//   - Instantiate a (smaller) space resource with a known size.
//   - Write out known values on the surface.
//   - Write out several Nodes (including nodes with AEDs).
//   - Confirm all nodes wrote to only to their space.
//   - Free all nodes, confirming space resource has original amount
//     of space (no leakage).
TestStatus
testNodeSpaceUsage(SpaceResource& space, 
                   LogExtent& logExtent)
{
    TestStatus returnVal = TEST_SUCCESS;  // Success by default.

    // We want to extract an extent from the space resource passed in
    //   for a smaller space resource.
    LogExtent testExtent;
    Error err(error_None);
    UINT32 numTestBlocks = 200;
    UINT32 totalSpaceBytes = logExtent.blockSize() * numTestBlocks;

    space.takeLogExtent(testExtent, totalSpaceBytes, err);

    if(err)
    { // If we could not get our test extent.
        returnVal = TEST_FAIL; 
#ifdef NODE_DEBUG
        errorMsg("Could not get memory for smaller space resource!");
        printError(err);
#endif
    }
    else
    { // We got the test extent.  Make a space resource for
        //   testing.
        LELSpaceResource littleSpace(*testExtent.partitionp()->logVolumep(), 
                                     srSpace_UnerasedOnly);
 
        littleSpace.putLogExtent(testExtent,err);

        if(err)
        { // If the space resource we created is in error.
            returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
            errorMsg("Could not create small space resource!");
            printError(err);
#endif
        }
        else
        { // We have our space resource.
            // Confirm correct space usage with this space resource
            //   on a series of Nodes.
            // First, zero out all bytes on the space resource (on
            //   the surface).
#ifdef NODE_DEBUG
            printf("Before zeroing surface, we have %ld available bytes.\n", 
                   littleSpace.availBytes());
#endif
            writeZerosToSpaceResource(littleSpace, err);
#ifdef NODE_DEBUG
            printf("After zeroing surface, we have %ld available bytes.\n", 
                   littleSpace.availBytes());
#endif

            if(err)
            { // If we could not zero out the surface.
#ifdef NODE_DEBUG
                errorMsg("Could not zero out the surface!");
                printError(err);
#endif
            }
            else
            { // We were able to zero out the surface.
#ifdef NODE_DEBUG
                printf("Before  writing AED node types, we have %ld available bytes.\n", 
                       littleSpace.availBytes());
#endif

                // Now, write out Nodes with zero, one, two, and three ADs.
                LogExtentList usedExtents(*logExtent.logVolumep());
                if((returnVal = 
                    writeAEDNodeTypes(littleSpace, usedExtents)) != TEST_SUCCESS)
                { // If we could not write our different node types.
#ifdef NODE_DEBUG
                    errorMsg("Could not write the AED node types!");
                    printError(err);
#endif
                }
                else
                { // We could write our sample AEDs.
#ifdef NODE_DEBUG
                    printf("After writing AED node types, we have %ld available bytes.\n", 
                           littleSpace.availBytes());
                    printf("and the list returned has infoLen of %ld (sum is %ld)\n", 
                           usedExtents.infoLen(), 
                           littleSpace.availBytes() + usedExtents.infoLen());
#endif
  
                    // Confirm the length of the blocks the Nodes are using
                    //   is exactly the space removed from the little space
                    //   resource.
                    if(totalSpaceBytes != (usedExtents.infoLen() + littleSpace.availBytes()))
                    { // If the total available bytes is not the sum of the bytes
                        //   in use and the bytes available.
                        // Logical loss/gain of space problem.
                        returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
                        printf("Space calculation problem!  %ld total, %ld available, %ld in use.\n", 
                               totalSpaceBytes, 
                               littleSpace.availBytes(), 
                               usedExtents.infoLen());
#endif
                    }
                    else
                    { // We have conservation of space.  :-)
  
                        // Confirm every block used by the Nodes has been written
                        //   to.
                        if((returnVal = confirmWrittenStatus(usedExtents, True)) != TEST_SUCCESS)
                        { // If not all the blocks in use are written.
#ifdef NODE_DEBUG
                            errorMsg("Not all the blocks in use are written!");
#endif
                        }
                        else
                        { // All our blocks are written.
    
                            // Confirm no other block not in the "used" list has
                            //   been written to.
                            if(returnVal = confirmAvailableBlocksNotWritten(littleSpace))
                            { // If any of the blocks outside this list are written.
#ifdef NODE_DEBUG
                                errorMsg("Blocks outside the list are written!");
                                printError(err);
#endif
                            }
                            else
                            { // Blocks outside the list are not written.
  
#ifdef NODE_DEBUG
                                printf("Before we return the space, %ld total, %ld available, %ld in use (available and use sum: %ld).\n", 
                                       totalSpaceBytes, 
                                       littleSpace.availBytes(), 
                                       usedExtents.infoLen(),
                                       littleSpace.availBytes() + usedExtents.infoLen());
#endif
                                // Free the blocks back to the space resource, and
                                //   confirm we have all the space back.
                                littleSpace.putLogExtentList(usedExtents,err);
                                if (err) {
                                    errorMsg("Could not put blocks back inside the space resource");
                                    printError(err);
                                }
#ifdef NODE_DEBUG
                                printf("After we return the space, %ld total, %ld available, %ld in use (available and use sum: %ld).\n", 
                                       totalSpaceBytes, 
                                       littleSpace.availBytes(), 
                                       usedExtents.infoLen(),
                                       littleSpace.availBytes() + usedExtents.infoLen());
#endif
                                if(littleSpace.availBytes() != totalSpaceBytes)
                                { // If we did not get all our bytes back.
                                    returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
                                    errorMsg("Did not get all space back!");
                                    printf("%ld total, %ld available, %ld in use.\n", 
                                           totalSpaceBytes, 
                                           littleSpace.availBytes(), 
                                           usedExtents.infoLen());
#endif
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return returnVal;
}

//------------------------------------------------------
// w r i t e Z e r o s T o S p a c e R e s o u r c e ( )
// Write out all zeros to the surface for all space in this space
//   resource.
void
writeZerosToSpaceResource(SpaceResource& space, Error err)
{
    // Create a byte array for one block.
    ByteArray byteArray(space.blockSize());
    // Make sure the byte array is all zeros.
    byteArray.clear();  // Set to zeros.

    LogExtentList clearedList(space.logVolume());
    LogExtent thisExtent;
    err = error_None;
    while(space.availBytes() && (!err))
    { // While there are still bytes available in this space
        //  resource.
        space.takeLogExtent(thisExtent, space.blockSize(), err);
        if(err)
        { // If we could not get this extent.
#ifdef NODE_DEBUG
            errorMsg("Could not another extent!");
            printError(err);
#endif
        }
        else
        { // We could get the extent.
            thisExtent.write(byteArray, err);
            if(err)
            { // If we could not write this byte array.
#ifdef NODE_DEBUG
                errorMsg("Could not write the byte array!");
                printError(err);
#endif
            }
            else
            { // We wrote the byte array.
                // Append this block to the cleared list.
                clearedList.append(thisExtent, err);
            }
        }
    }
    if(!err)
    { // If we were able to clear all of the space resource.
        // We want to return the extents to that resource.
        space.putLogExtentList(clearedList,err);
        if (err) {
#ifdef NODE_DEBUG
            errorMsg("Could not return extents to resource!");
            printError(err);
#endif
        }
    }
}

//--------------------------------------------
// c o n f i r m W r i t t e n S t a t u s ( )
// Conform all blocks in the extent list passed in are or are NOT 
//   written to, depending on the status value passed in.  
// NOTE: We assume all zeros mean they have NOT been written to,
//   and non-zeros anywhere in the sector meaning they are written.
TestStatus
confirmWrittenStatus(LogExtentList& list, Boolean expected)
{
    // We want to read every extent in this list from the surface
    //   and confirm at least one byte is non-zero.
    Error err = error_None;
    TestStatus returnVal = TEST_SUCCESS; 

    LogExtent* thisExtent;

    UINT32 numExtents = list.numLogExtents();
    for(UINT32 i=0; i< numExtents; i++)
    { // For each extent in the list.
        thisExtent = list.at(i, err);
        if(err)
        { // If we could not get this extent.
            returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
            errorMsg("Could extent at this location!");
            printError(err);
#endif
        }
        else
        { // We could get this extent.
            Boolean isWritten;
            isWritten = extentIsWritten(*thisExtent, err);

            if(err)
            { // If we can't determine if the extent is written.
                returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
                errorMsg("Cannot determine if extent is written!");
                printError(err);
#endif
            }
            else
            { // We know if the extent is written.
                if(isWritten != expected)
                { // If this written status was not expected, error.
                    returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
                    errorMsg("Written status not expected!");
                    list.print(0, "List extents:");
                    thisExtent->print(0, "Unexpected write status for:");
#endif
                    break;  // Don't bother testing more.
                }
            }
        }
    }
    return returnVal;
}
          
//----------------------------------
// e x t e n t I s W r i t t e n ( )
// Return non-zero if this extent passed in has a non-zero value
//   when read from the surface.
Boolean
extentIsWritten(LogExtent& logExtent, Error err)
{
    // Read the extent in.
    ByteArray byteArray(logExtent.infoLen());

    Boolean returnVal = False;  // By default, we are not written.
    logExtent.read(byteArray, err);

    if(err)
    { // If we could not read this byte array from the surface.
#ifdef NODE_DEBUG
        errorMsg("Could not read byte array from the surface!");
        printError(err);
#endif
    }
    else
    { // We could read the byte array.
        
        UINT32 i;
        BYTE_PTR bp = (BYTE_PTR) byteArray;
        for(i = 0; i < byteArray.length(); i++)
        { // For each byte in this byte array.
            // Is this byte array read?
            if(*(bp+i))
            { // If this char has a value.
#ifdef NODE_DEBUG
                printf("HOORAY!  The block is written! at i = %ld\n", i);
#endif
                returnVal = True;  // We found a written char.
                break;  // We found a written char.
            }
        }
#ifdef 0  // Prints out too much.NODE_DEBUG
        if(i < byteArray.length())
        { // Broke prematurely (we are written).
            printf("Found first non-zero byte at %ld, value %ld (array is %ld long).\n", 
                   i, (UINT32)((BYTE_PTR)byteArray)[i], byteArray.length());
            logExtent.print(0, "Extent read:");
        }
        else
        { // Process all of loop.
            printf("byteArray of length %ld is entirely zeros (not written).\n",
                   byteArray.length());
            logExtent.print(0, "Extent read:");
        }
#endif
    }
    return returnVal;
}

//--------------------------------------------------------------------
// c o n f i r m A v a i l a b l e B l o c k s N o t W r i t t e n ( )
// Confirm the available blocks in the space resource passed in are
//   NOT written to.
// NOTE: We assume that a block will have all zero values if it
//   is not written to.
TestStatus
confirmAvailableBlocksNotWritten(SpaceResource& space)
{
    // Create a list of all extents in the space resource passed
    //   in, and pass it to confirmWrittenStatus.
    LogExtentList checkList(space.logVolume());
    LogExtent thisExtent;
    TestStatus returnVal = TEST_SUCCESS;
    Error err;

    while(space.availBytes() && (!err))
    { // While there are still bytes available in this space
        //  resource.
        space.takeLogExtent(thisExtent, space.blockSize(), err);
        if(err)
        { // If we could not get this extent.
            returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
            errorMsg("Could not get another extent!");
            printError(err);
#endif
            break;  // Don't try anymore.
        }
        else
        { // We got the extent.  Add it to our list.
            checkList.append(thisExtent, err);
            if(err)
            { // If we could not append it to our list.
                returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
                errorMsg("Could not append to the list!");
                printError(err);
#endif
                break; // Don't try anymore.
            }
        }
    }
    if(!err)
    { // If we did not have problems getting all the extents.
        // Confirm none of the extents are written.
        returnVal = confirmWrittenStatus(checkList, False);

    // Now, we must put all these extents back.
        space.putLogExtentList(checkList,err);
        if(err)
        { // If we could not put all extents back.
            returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
            errorMsg("Could not return all extents!");
            printError(err);
#endif
        }
    }
    return returnVal;
}

//--------------------------------------
// w r i t e A E D N o d e T y p e s ( )
// Write several Nodes objects to parts of the SpaceResource
//   passed in, and return a list of all extents we wrote to
//   in the LogExtentList passed in.
TestStatus
writeAEDNodeTypes(SpaceResource& space, 
                  LogExtentList& logExtentList)
{
    TestStatus returnVal = TEST_SUCCESS;  // Succeed by default.

    logExtentList.clear();  // Get rid of other possible extents.

    // Now, we want to extract the first couple of blocks from
    //   the space resource so we are not right on the left side
    //   (the nodes cannot use them for writing).

    LogExtent bufferSpace;
    Error err = error_None;
    space.takeLogExtent(bufferSpace, 3 * space.blockSize(), err);
    bufferSpace.print(0, "Took buffer space:");

    if(err)
    { // If we could not get the buffer space on the left.
        returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
        errorMsg("Could not get buffer space!");
        printError(err);
#endif
    }
    else
    { // We can get the buffer space.
        for(UINT32 aedsThisNode = 0; aedsThisNode < 4; aedsThisNode++)
        { // For nodes with zero, one, two, and three AEDs.
      
            err = writeNodeAndAppendToEL(space, aedsThisNode, logExtentList);

            if(err)
            { // If we could not write this node.
                returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
                printf("ERROR: Could not write node with %ld AEDs!\n", aedsThisNode);
                printError(err);
#endif
                break;
            }
        }

        // At this point, we wrote our nodes and appended them all
        //   to this LogExtentList.

        // Now, we want free the buffer space back to the 
        //   SpaceResource (we just didn't want any nodes to
        //   write to it).
        space.putLogExtent(bufferSpace,err);
        if(err)
        { // If we could not return buffer space
            returnVal = TEST_FAIL;
#ifdef NODE_DEBUG
            printf("ERROR: Could not return buffer space to space resource!\n");
            printError(err);
#endif
        }
    }
    return returnVal;
}


//------------------------------------------------
// w r i t e N o d e A n d A p p e n d T o E L ( )
// Write out a node with the number of AEDs passed in.
//   Append all blocks used to the extent list passed in, including
//   the extent for the FileEntry portion of the Node.
Error
writeNodeAndAppendToEL(SpaceResource& space,
                       UINT32 numAEDs,
                       LogExtentList& list)
{
    Error err = error_None;

    // Get one sector for the file entry portion of a node.
    LogExtent descriptorPart;
    space.takeLogExtent(descriptorPart, space.blockSize(), err);
    if(err)
    { // If we could not get the buffer space on the left.
#ifdef NODE_DEBUG
        errorMsg("Could not get block for descriptor!");
        printError(err);
#endif
    }
    else
    { // We got the sector for the descriptor.

        // We want to write out this block.
        // RECALL: This function writes the node and reads it back,
        //   confirming integrity.
        ADType adType = ad_Long;
        UINT32 numBodyADs = getMaxNumItemsInFileEntry(descriptorPart, 
                                                      adType,
                                                      0)
            + numAEDs * getMaxNumItemsInAED(descriptorPart,
                                            ad_Long);
  
        Node thisNode(space.logVolume());
        err = populateNode(space, 
                           thisNode, 
                           0x0000FFFF,      // Unique ID.
                           0,               // Num EABytes.
                           adType,            
                           0,               // Data length.
                           numBodyADs, 
                           0);              // Num tail ADs.

        if(err)
        { // If we could not populate the node.
#ifdef NODE_DEBUG
            errorMsg("Could not populate node!");
            printError(err);
#endif
        }
        else
        { // We populated the node.
        
            // Write the node out.
            thisNode.toDisk(descriptorPart, space, err);

            if(err)
            { // If we could not write the node out.
#ifdef NODE_DEBUG
                errorMsg("Could not write out the node!");
                printError(err);
#endif
            }
            else
            { // We could write the node out.
                // At this point, we must append any space the node
                //   used during the write.
                // We must append this extent to our list.
                list.append(descriptorPart, err);
#ifdef NODE_DEBUG
                printf(0, "Added this desriptor block.");
#endif
    
                if(err)
                { // If we could not append our descriptor extent to our
                    //   extent list.
#ifdef NODE_DEBUG
                    errorMsg("Could not append descriptor block to list!");
                    printError(err);
#endif
                }
                else
                { // We appended our descriptor block to the list.
            
                    list.concat(thisNode.diskSpace(), err);
                    if(err)
                    { // If we could not concatenate the lists.
#ifdef NODE_DEBUG
                        errorMsg("Could not concatenate logExtentLists!");
                        printError(err);
#endif
                    }
                }
            }
        }
    }
    return err;
}

//------------------------------------------
// t e s t N o d e C o r n e r C a s e s ( )
// This is the highest level of half our Nodes testing:  Testing the Nodes corner
//   cases.
// Create Nodes corner cases and test them one at a time.
TestStatus
testNodeCornerCases(SpaceResource& space, LogExtent& logExtent)
{
    TestStatus returnStatus = TEST_SUCCESS;
    TestStatus status;

    // We need to test node corner cases for each type of 
    //   information we might store (embedded data, or short,
    //   long, and extended ADs.)

    if(global_testEmbeddedData)
    {
        // Test with embedded data.
        if(global_continue || (returnStatus == TEST_SUCCESS) ) 
            checkTest(
                testNodeCornerCaseSuite(space, logExtent, ad_Embedded),
                returnStatus,
                "Failed Embedded Data test suite!",    // Failure message.
                NULL);                                 // Success message.
    }

    if(global_testShortADs)
    {
        // Test with Short ADs.
        if(global_continue || (returnStatus == TEST_SUCCESS) ) 
            checkTest(
                testNodeCornerCaseSuite(space, logExtent, ad_Short),
                returnStatus,
                "Failed Short AD test suite!",         // Failure message.
                NULL);                                 // Success message.
    }
  
    if(global_testLongADs)
    {
        // Test with Long ADs.
        if(global_continue || (returnStatus == TEST_SUCCESS) ) 
            checkTest(
                testNodeCornerCaseSuite(space, logExtent, ad_Long),
                returnStatus,
                "Failed Long AD test suite!",          // Failure message.
                NULL);                                 // Success message.
    }

    if(global_testExtendedADs)
    {
        // Test with Extended ADs.
        if(global_continue || (returnStatus == TEST_SUCCESS) ) 
            checkTest(
                testNodeCornerCaseSuite(space, logExtent, ad_Extended),
                returnStatus,
                "Failed Extended AD test suite!",      // Failure message.
                NULL);                                 // Success message.
    }

    // NOTE:  Only one test suite above needs to fail for this
    //   function to return failure (but we may still want to run all
    //   of the above tests).
    return returnStatus;
}

//--------------------------------------------------
// t e s t N o d e C o r n e r C a s e S u i t e ( )
// Identify and run all the corner cases for a Node storing the
//   type of data passed in. 
// The above corner cases must be calculated relative to EASpace,
//   which we will also dynamically calculate (in 4 byte increments):
//   - no EA space
//   - minimal EA space (4 bytes)
//   - half EA space
//   - all EA space minus room for one of AD type
//   - all EA space
//   - all EA space, also ADs/embedded data (should fail)
//   - all EA space plus one (should fail)
TestStatus
testNodeCornerCaseSuite(SpaceResource& space, 
                        LogExtent& logExtent, 
                        ADType adType)
{
    TestStatus returnStatus = TEST_SUCCESS;  // Success by default.

    // Get the number of bytes required to store one AD (or one
    //   embedded data byte) for whatever we are storing.
    UINT32 oneADBytes = numBytesForAD(adType);

    // Find out how many FileEntry bytes are available (total).
    UINT32 maxFEBytes = getMaxFileEntryBytes(logExtent, adType);

    // Find out how much EA Space we could use (maximum, 4 byte boundary).
    UINT32 maxEABytes = getMaxEABytes(logExtent, adType);

    // Number of EABytes to be written this time.
    UINT32 writeEABytes = 0;

    // Try corner cases with no EA space.
    writeEABytes = 0;
    if(global_continue || (returnStatus == TEST_SUCCESS) ) 
        checkTest(
            testNodeADCornerCaseSuite(space, logExtent, adType, 
                                      writeEABytes,
                                      // Can write ADs/embedded data?
                                      maxFEBytes >= (writeEABytes + oneADBytes), 
                                      TEST_SUCCESS,    // emptyNodeExpectedStatus
                                      TEST_SUCCESS),   // writeADsExpectedStatus
            returnStatus,
            "Failed No EA Space test suite!",      // Failure message.
            NULL);                                 // Success message.

    if(maxEABytes >= 4)
    { // If we can write more than four EA bytes, then we can test
        //   the minimal number of EA bytes to be written.

        // Try corner cases with minimal EA space. (four bytes).
        writeEABytes = 4;
        if(global_continue || (returnStatus == TEST_SUCCESS) ) 
            checkTest(
                testNodeADCornerCaseSuite(space, logExtent, adType, 
                                          writeEABytes, 
                                          // Can write ADs/embedded data?
                                          maxFEBytes >= (writeEABytes + oneADBytes), 
                                          TEST_SUCCESS,    // emptyNodeExpectedStatus
                                          TEST_SUCCESS),   // writeADsExpectedStatus
                returnStatus,
                "Failed Minimal EA Space test suite!", // Fail message.
                NULL);                                 // Success message.

        if(maxEABytes >= 8)
        { // If we could write at least 8 EA bytes, then we can try
            //   all or half the space with EA bytes (implicitly done
            //   already if we could only write 0 or 4 bytes).

            // Try corner cases with half EA space.
            writeEABytes = maxEABytes/2;
            if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                checkTest(
                    testNodeADCornerCaseSuite(space, logExtent, adType, 
                                              writeEABytes, 
                                              // Can write ADs/embedded data?
                                              maxFEBytes >= (writeEABytes + oneADBytes), 
                                              TEST_SUCCESS,    // emptyNodeExpectedStatus
                                              TEST_SUCCESS),   // writeADsExpectedStatus
                    returnStatus,
                    "Failed Half EA Space test suite!",    // Fail message.
                    NULL);                                 // Success message.

            // Try corner cases with all EA space (none of ADType).
            writeEABytes = maxEABytes;
            if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                checkTest(
                    testNodeADCornerCaseSuite(space, logExtent, adType, 
                                              writeEABytes, 
                                              0, // Cannot store ADs/embedded data.
                                              TEST_SUCCESS,    // emptyNodeExpectedStatus
                                              TEST_SUCCESS),   // writeADsExpectedStatus
                    returnStatus,
                    "Failed All EA Space test suite!", // Fail message.
                    NULL);                                 // Success message.

            if(maxEABytes >= 12)
            { // If we could write at least 12 bytes, then we can try
                //   with all EA space minus one (four bytes), which
                //   would have implicitly been tested if we only could
                //   write a maximum of 8 bytes.

                // Try corner cases with all EA space minus one of ADType.
                writeEABytes = 
                    // Does one AD fit in the byte rounding of EA?
                    ((maxFEBytes - maxEABytes) >= oneADBytes) ?
                    // If so, write maxEABytes.
                    maxEABytes :
                    // Otherwise, set EABytes to be where one AD fits.
                    ROUND_DOWN_BY_FOUR(maxFEBytes - oneADBytes);
          
                ROUND_UP_BY_FOUR(oneADBytes);
                if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                    checkTest(
                        testNodeADCornerCaseSuite(space, logExtent, adType, 
                                                  // Leave room for one AD or embedded data.
                                                  writeEABytes,
                                                  1, // Can store ADs/embedded data.
                                                  TEST_SUCCESS,    // emptyNodeExpectedStatus
                                                  TEST_SUCCESS),   // writeADsExpectedStatus
                        returnStatus,
                        "Failed ALL EA Space Minus One test suite!", // Fail message.
                        NULL);                                       // Success message.
            }
        }
    }

    // Try the corner cases with all EA space, plus attempting
    //   to write ADs/embedded data.
    writeEABytes = maxEABytes;
    if(global_continue || (returnStatus == TEST_SUCCESS) ) 
        checkTest(
            testNodeADCornerCaseSuite(space, logExtent, adType, 
                                      writeEABytes,
                                      1,  // Can store ADs/embedded data.
                                      TEST_SUCCESS,  // Succeeds without ADs.
                                      TEST_FAIL),    // We expect failure with ADs. 
            returnStatus,
            "Failed (Wrongly Passed) All EA Space Plus ADs test suite!", // Fail message.
            NULL);                                 // Success message.

    // Try corner cases with all EA space plus one (plus 4 bytes) (should fail).
    writeEABytes = maxEABytes + 4;
    if(global_continue || (returnStatus == TEST_SUCCESS) ) 
        checkTest(
            testNodeADCornerCaseSuite(space, logExtent, adType, 
                                      writeEABytes,
                                      0,  // Cannot store ADs/embedded data.
                                      TEST_FAIL,   // Fails without ADs.
                                      TEST_FAIL),  // Fails with ADs.
            returnStatus,
            "Failed (Wrongly Passed) Too Much EA Space test suite!", // Fail message.
            NULL);                                 // Success message.

    return returnStatus;
}

//--------------------------------------------------
// t e s t N o d e C o r n e r C a s e S u i t e ( )
// Given the amount of EA space we should use and the AD type
//   we should test, run this Node state through a series of
//   corner cases.
// The corner cases include:
//   - no data/ADs
//   - no data/ADs plus one
//   - half-full of data/ADs
//   - full minus one
//   - full
//   - full plus one (should fail embedded data)
//
// Additionally, Test those corner cases where we write out ADs that 
//   MUST overflow into AEDs.
//
// The corner cases include:
//   - ONE+ AD in the FileEntry, AED has one AD.
//   - ONE+ AD in the FileEntry, AED half full.
//   - ONE+ AD in the FileEntry, AED full minus one.
//   - ONE+ AD in the FileEntry, AED full.
//   - ONE+ AD in the FileEntry, AED full, AED2 has one.
//   - ONE+ AD in the FileEntry, AED full, AED2 half full.
//   - ONE+ AD in the FileEntry, AED full, AED2 full minus one.
//   - ONE+ AD in the FileEntry, AED full, AED2 full.
//   - ONE+ AD in the FileEntry, AED full, AED2 full, AED3 has one.
//   - ONE+ AD in the FileEntry, AED full, AED2 full, AED3 half full.
//   - ONE+ AD in the FileEntry, AED full, AED2 full, AED3 full minus one.
//   - ONE+ AD in the FileEntry, AED full, AED2 full, AED3 full.
//
// NOTE:  AED4..N should be the same as AED3, so they are not tested.
//
// NOTE:  The number of ADs/embedded data that actually fits
//   into the FileEntry is determined by the amount of EASpace.
//   Since that space is variable and set outside our scope,
//   we are implicitly testing the following (called by the varied
//   setting of the EASpace):
//
//   - Run the above corner cases with TWO ADs in the FileEntry.
//   - Run the above corner cases with half the possible ADs in the FileEntry.
//   - Run the above corner cases with all possible ADs in the FileEntry.
//
// NOTE:  As an ancillary goal, we populate the node with
//   uniqueID corner cases (no value, one, highest value, etc.)
//
TestStatus
testNodeADCornerCaseSuite(SpaceResource& space, 
                          LogExtent& logExtent, 
                          ADType adType,
                          UINT32 numEABytes,
                          UINT16 canWriteADsFlag,
                          TestStatus emptyNodeExpectedStatus,
                          TestStatus writeADsExpectedStatus)
{
    TestStatus returnStatus = TEST_SUCCESS;

    // We want to instantiate a Node with the state passed
    //   in, PLUS conditions with various amounts of embedded
    //   data or ADs.

    // Hold the number of items (ADs or embedded data bytes) for
    //   this test.
    UINT32 numItems = 0;

    // Create a node with no data/ADs.
    numItems = 0;
    if(global_continue || (returnStatus == TEST_SUCCESS) ) 
        checkTest(
            distributeBodyAndTailAndTest(
                space, 
                logExtent, 
                0x00000002,         // Unique id.
                numEABytes, 
                adType, 
                numItems,           // Number of ADs or embedded data.
                emptyNodeExpectedStatus),
            returnStatus,
            "Failed Empty Node Test!", // Fail message.
            NULL);                     // Success message.

    if(canWriteADsFlag)
    { // If we are told to attempt to write embedded data or ADs.

        // Find out the maximum number of items of this type we can write out.
        UINT32 maxFEItems = getMaxNumItemsInFileEntry(logExtent, 
                                                      adType, 
                                                      numEABytes);

        // Test all meaningful ADs/embedded data for the range
        //   that fits into our FileEntry.
        numItems = maxFEItems;
        if(global_continue || (returnStatus == TEST_SUCCESS) ) 
            checkTest(
                testADRangeCornerCases(space,
                                       logExtent,
                                       adType,
                                       numEABytes,
                                       1,  // Test one or more.
                                       numItems, // Test the max in a FE.
                                       writeADsExpectedStatus),
                returnStatus,
                "Failed ADRange Corner Cases!", // Fail message.
                NULL);

        // Now that we have tested all the reasonable corner cases of
        //   ADs/embedded data, try too much embedded data.
        //   NOTE:  We can't have too many ADs, because they always 
        //     overflow to AEDs.
        if(adType == ad_Embedded)
        {
            numItems = maxFEItems + 1;
            if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                checkTest(
                    distributeBodyAndTailAndTest(
                        space, 
                        logExtent, 
                        0x77777777,         // Unique id (median value).
                        numEABytes, 
                        adType, 
                        numItems,           // Number of ADs or embedded data.
                        TEST_FAIL),
                    returnStatus,
                    "Failed (wrongly passed) Node with too many ADs/embedded data!", // Fail message.
                    NULL);                     // Success message.
        }
        else
        { // We have ADs.
  
            // At this point, we have exhaustively tested the FileEntry
            //   with all relevant corner cases for the number of ADs
            //   we can store.  However, we have not tested ANY ADs in
            //   overflow AEDs.  (Test them now).
            UINT32 maxAEDItems = getMaxNumItemsInAED(logExtent, adType);
  
            // Test all meaningful ADs/embedded data for the range
            //   that fits into our FIRST overflow AED.
            numItems = maxFEItems + maxAEDItems;
            if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                checkTest(
                    testADRangeCornerCases(space,
                                           logExtent,
                                           adType,
                                           numEABytes,
                                           maxFEItems + 1, // Start in first AED.
                                           numItems, // Test the max in a FE.
                                           writeADsExpectedStatus),
                    returnStatus,
                    "Failed One AED Corner Cases!", // Fail message.
                    NULL);
  
            // Test all meaningful ADs/embedded data for the range
            //   that fits into our SECOND overflow AED.
            numItems = maxFEItems + (2 * maxAEDItems);
            if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                checkTest(
                    testADRangeCornerCases(space,
                                           logExtent,
                                           adType,
                                           numEABytes,
                                           maxFEItems + maxAEDItems + 1,  // Start in second AED.
                                           numItems, // Test the max in a FE.
                                           writeADsExpectedStatus),
                    returnStatus,
                    "Failed TWO AEDs Corner Cases!", // Fail message.
                    NULL);
  
            // Test all meaningful ADs/embedded data for the range
            //   that fits into our THIRD overflow AED.
            numItems = maxFEItems + (3 * maxAEDItems);
            if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                checkTest(
                    testADRangeCornerCases(space,
                                           logExtent,
                                           adType,
                                           numEABytes,
                                           maxFEItems + (2 * maxAEDItems) + 1,  // Start in third AED.
                                           numItems, // Test the max in a FE.
                                           writeADsExpectedStatus),
                    returnStatus,
                    "Failed THREE AEDs Corner Cases!", // Fail message.
                    NULL);
        }
    }
    return returnStatus;
}

//--------------------------------------------
// t e s t R a n g e C o r n e r C a s e s ( )
// Given the node state passed in and the range of ADs/embedded
//   data we should test, test these corner cases:
// The corner cases include:
//   - low data/ADs
//   - low data/ADs plus one
//   - mid between low and high data/ADs
//   - high minus one data/ADs
//   - high data/ADs
// NOTE:  As an ancillary goal, we populate the node with
//   uniqueID corner cases (no value, one, highest value, etc.)
TestStatus
testADRangeCornerCases( SpaceResource& space, 
                        LogExtent& logExtent, 
                        ADType adType,
                        UINT32 numEABytes,
                        UINT32 lowNum, 
                        UINT32 highNum,
                        TestStatus expectedStatus)
{
    // We want to test various numbers of ADs within the range specified.
    UINT32 numItems;
    TestStatus returnStatus = TEST_SUCCESS;

    // Create a node with LOW data/AD.
    numItems = lowNum;
    if(global_continue || (returnStatus == TEST_SUCCESS) ) 
        checkTest(
            distributeBodyAndTailAndTest(
                space, 
                logExtent, 
                0xFFFFFFFF,         // Unique id (max).
                numEABytes, 
                adType, 
                numItems,           // Number of ADs or embedded data.
                expectedStatus),
            returnStatus,
            "Failed Node with lowNum AD Test!", // Fail message.
            NULL);                     // Success message.

    if(highNum > lowNum)
    { // If we can write out more items than we have written.
      
        // Write out the maximum number of items we can write.
        numItems = highNum;
        if(global_continue || (returnStatus == TEST_SUCCESS) ) 
            checkTest(
                distributeBodyAndTailAndTest(
                    space, 
                    logExtent, 
                    0x00000001,         // Unique id (minimum + 1).
                    numEABytes, 
                    adType, 
                    numItems,           // Number of ADs or embedded data.
                    expectedStatus),
                returnStatus,
                "Failed Node with highNum ADs Test!", // Fail message.
                NULL);                     // Success message.

        if(highNum > (lowNum+1))
        { // If we can write more than the two combinations we
            //   already wrote out, write out half
            //   of the items.  (Done implicitly already unless we 
            //   have a range of three or more items).

            // Write out half of the maximum number of items we can write.
            numItems = highNum/2 + (highNum%2); // Get the odd one.
            if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                checkTest(
                    distributeBodyAndTailAndTest(
                        space, 
                        logExtent, 
                        0xFFFFFFFE,         // Unique id (maximum minus one).
                        numEABytes, 
                        adType, 
                        numItems,           // Number of ADs or embedded data.
                        expectedStatus),
                    returnStatus,
                    "Failed Node with Half num Test!", // Fail message.
                    NULL);                     // Success message.

            if(highNum > (lowNum+2))
            { // If we have a range of 4 or more, then we must
                //   attempt to write maxItems minus one (done implicitly
                //   unless we can write 4 or more items out).

                // Write out highNum minus one.
                numItems = highNum-1;
                if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                    checkTest(
                        distributeBodyAndTailAndTest(
                            space, 
                            logExtent, 
                            0x0000FFFF,         // Unique id (intermediate value).
                            numEABytes, 
                            adType, 
                            numItems,           // Number of ADs or embedded data.
                            expectedStatus),
                        returnStatus,
                        "Failed Node highNum Minus One Test!", // Fail message.
                        NULL);                     // Success message.

                // Write out lowNum plus one.
                numItems = lowNum+1;
                if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                    checkTest(
                        distributeBodyAndTailAndTest(
                            space, 
                            logExtent, 
                            0x0000FFFF,         // Unique id (intermediate value).
                            numEABytes, 
                            adType, 
                            numItems,           // Number of ADs or embedded data.
                            expectedStatus),
                        returnStatus,
                        "Failed Node lowNum Plus One Test!", // Fail message.
                        NULL);                     // Success message.
            }
        }
    }
    return returnStatus;
}

//------------------------------------------------------------
// d i s t r i b u t e B o d y A n d T a i l A n d T e s t ( )
// Given the node state passed in, distribute the node ADs over
//   the body and tail to confirm that those corner cases work.
// Test:
//   No body, all tail.
//   One body, rest tail.
//   Half body, half tail.
//   Rest body, one tail.
//   All body, no tail.
TestStatus
distributeBodyAndTailAndTest(SpaceResource& space, 
                             LogExtent& logExtent, 
                             UINT64 uniqueID, 
                             UINT32 numEABytes, 
                             ADType adType, 
                             UINT32 numItems, 
                             TestStatus  expectedStatus)
{
    TestStatus returnStatus = TEST_SUCCESS;  // Success by default.

    // Make a boolean value (flag to see if we have ADs or embedded
    //   data).
    UINT32 haveADs = (adType!=ad_Embedded);

    // Make and test the node with all data in the tail.
    if(global_continue || (returnStatus == TEST_SUCCESS) ) 
        checkTest(
            makeNodeAndTest(
                space,
                logExtent,
                uniqueID,              // Unique id.
                numEABytes,            // Num EA Bytes.
                adType,                // Type of data.
                (haveADs)?0:numItems,  // Number of bytes for embedded data.
                0,                     // Number of body ADs.
                (haveADs)?numItems:0,  // Number of tail ADs.
                expectedStatus         // Expected status.
                ),
            returnStatus,
            "Failed Distribute All Tail Node Test!", // Fail message.
            NULL);                                   // Success message.

    if(haveADs && numItems)
    { // If we have ADs (not embedded data), and we actually have
        //   at least one item to distribute, then we can actually
        //   try to distribute it to the body AND the tail.  (The
        //   above case is the only case we can do with embedded
        //   data).

        // Make and test the node with all data in the body.
        if(global_continue || (returnStatus == TEST_SUCCESS) ) 
            checkTest(
                makeNodeAndTest(
                    space,
                    logExtent,
                    uniqueID,              // Unique id.
                    numEABytes,            // Num EA Bytes.
                    adType,                // Type of data.
                    0,                     // We aren't doing embedded data.
                    numItems,              // Number of body ADs.
                    0,                     // Number of tail ADs.
                    expectedStatus         // Expected status.
                    ),
                returnStatus,
                "Failed Distribute All Body Node Test!", // Fail message.
                NULL);                                   // Success message.

        if(numItems > 1)
        { // If we only had one item, we can't distribute it any more
            //   than we did.  However, if there is more than one, 
            //   then we can divide the number (somewhat) evenly
            //   between the body and the tail.

            // Divide the ADs evenly between the body and the tail.
            if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                checkTest(
                    makeNodeAndTest(
                        space,
                        logExtent,
                        uniqueID,                // Unique id.
                        numEABytes,              // Num EA Bytes.
                        adType,                  // Type of data.
                        0,                       // We aren't doing embedded data.
                        numItems/2+(numItems%2), // Half the ADs (plus the odd one).
                        numItems/2,              // Number of tail ADs.
                        expectedStatus           // Expected status.
                        ),
                    returnStatus,
                    "Failed Distribute Even Body/Tail Node Test!", // Fail message.
                    NULL);                                   // Success message.

            if(numItems > 3)
            { // If we have more than three items, we can put ONE
                //   item in the body, rest in the tail.  We can also
                //   put one in the tail, rest in the body.  This test
                //   was (implicitly) already done if we have 2 or 3 items.

                // Put one in the body, rest in the tail.
                if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                    checkTest(
                        makeNodeAndTest(
                            space,
                            logExtent,
                            uniqueID,              // Unique id.
                            numEABytes,            // Num EA Bytes.
                            adType,                // Type of data.
                            0,                     // We aren't doing embedded data.
                            1,                     // Number of body ADs.
                            numItems-1,            // Number of tail ADs.
                            expectedStatus         // Expected status.
                            ),
                        returnStatus,
                        "Failed Distribute One Body/Rest Tail Node Test!", // Fail message.
                        NULL);                                   // Success message.
  
                // Put one in the tail, rest in the body.
                if(global_continue || (returnStatus == TEST_SUCCESS) ) 
                    checkTest(
                        makeNodeAndTest(
                            space,
                            logExtent,
                            uniqueID,              // Unique id.
                            numEABytes,            // Num EA Bytes.
                            adType,                // Type of data.
                            0,                     // We aren't doing embedded data.
                            numItems-1,            // Number of body ADs.
                            1,                     // Number of tail ADs.
                            expectedStatus         // Expected status.
                            ),
                        returnStatus,
                        "Failed Distribute One Tail/Rest Body Node Test!", // Fail message.
                        NULL);                                   // Success message.
            }
        }
    }
    return returnStatus; 
}

//---------------------------------------------------------------------
// Instantiate a node and send it off for integrity testing and write/read
//   testing.
// NOTE:  We don't test the Node interface functions, but merely the
//   integrity of the state for this node passed in, including the preservation
//   of that integrity from write/read operations.
// NOTE:  We want to pass in both the data length and the number of ADs
//   so that the Node class is forced to reconcile the states at write
//   time.
// NOTE:  We use the expectedStatus parameter to determine whether or not we
//   succeeded in our Node state check.  For example, if we expect success,
//   TEST_SUCCESS is returned when the Node checks out with TEST_SUCCESS.  
//   However, if we expect TEST_FAIL, then TEST_SUCCESS is returned from 
//   this function when we actually get TEST_FAIL from populating 
//   the Node.
// NOTE: This is the lowest level test function for Nodes, which
//   instantiates a Node even in error state (if numBodyADs set and
//   dataLength set).  Thus, this test may be deliberately set up
//   to fail.
TestStatus
makeNodeAndTest(SpaceResource& space, 
                LogExtent& logExtent, 
                UINT64 uniqueID, 
                UINT32 numEABytes, 
                ADType adType, 
                UINT32 dataLength, 
                UINT32 numBodyADs, 
                UINT32 numTailADs,
                TestStatus  expectedStatus)
{
    // Instantiate a node to test.
    Node node(space.logVolume());
    TestStatus status;

    Error err;
  
#ifdef NODE_DEBUG
    printf("Populate Node with %lu EA bytes, %lu ADType, %lu embedded data, %lu body ADs, %lu tail ADs, expect: %ld\n", numEABytes, (UINT32) adType, dataLength, numBodyADs, numTailADs, expectedStatus);
#endif

    err = populateNode(space, node, uniqueID, numEABytes, adType, dataLength, 
                       numBodyADs, numTailADs);

    if(!err)
    { // If we were able to populate the node.
#ifdef NODE_DEBUG
        printf("VERIFY Node integrity!\n");
#endif

        // NOTE:  We don't want to checkTest because the upper
        //   level may expect this to fail (intentional verify failure).
        status = testNodeIntegrity(node, logExtent, adType);

        if(status == TEST_SUCCESS)
        { // If this node has integrity, test it on a write/read.
#ifdef NODE_DEBUG
            printf("VERIFY Node write/read!\n");
#endif
            status = testNodeWriteRead(space, logExtent, node, adType);
        }
#ifdef NODE_DEBUG
        else
        { 
            printf("Node does NOT have integrity.\n");
            printf("Node STATE: %s", (CONST_CHAR_PTR)node.getState());
        }
#endif
    }
  
    if(status == expectedStatus)
    { // If we got the status we expected.
        status = TEST_SUCCESS;  // We succeeded in this test (we got what we expected).
    }

#ifdef NODE_DEBUG
    printf("Returning from makeNodeAndTest, status is %ld\n", status);
#endif
    return status;
}

//------------------------------
// g e t M a x E A B y t e s ( )
// Return the maximum number of EA bytes we can fit in a FileEntry,
//   assuming no ADs or embedded data.
// NOTE:  Because EA space must be in multiples of 4 bytes, this
//   function will ONLY return a multiple of four bytes.
UINT32 getMaxEABytes(LogExtent& logExtent, ADType adType)
{
    UINT32 numBytes = getMaxFileEntryBytes(logExtent, adType);

    // Now, we must round down to the biggest four-byte multiple that
    //   will fit in this number.
    numBytes = ROUND_DOWN_BY_FOUR(numBytes);
#ifdef NODE_DEBUG
    printf("--------getMaxEABytes() returns %ld (adType is %ld).\n", 
           numBytes, (UINT32)adType);
#endif

    return numBytes;
}

//--------------------------------------------
// g e t M a x F i l e E n t r y B y t e s ( )
// Return the maximum number of bytes available in the FileEntry.
UINT32
getMaxFileEntryBytes(LogExtent& logExtent, ADType adType)
{
    // Make a FileEntry.
    FileEntry fileEntry(0, adType);
    UINT32 numBytes = fileEntry.unusedBytes(logExtent.blockSize());

#ifdef NODE_DEBUG
    printf("--------getMaxFileEntryBytes() returns %ld (adType is %ld).\n", 
           numBytes, (UINT32)adType);
#endif

    return numBytes;
}

//------------------------------------------------------
// g e t M a x N u m I t e m s I n F i l e E n t r y ( )
// Given the FileEntry state passed in, return the maximum number
//   of items of the ADType specified that we can fit into this
//   FileEntry (assuming no overflow AED).
UINT32
getMaxNumItemsInFileEntry( LogExtent& logExtent, 
                           ADType adType, 
                           UINT32 numEABytes)
{
    FileEntry fileEntry(0, adType);

    fileEntry.setEALen(logExtent.blockSize(), numEABytes);

    return fileEntry.maxADTEnts(logExtent.blockSize(), adType);
}

//------------------------------------------
// g e t M a x N u m I t e m s I n A E D ( )
// Given the AED state passed in, return the maximum number
//   of items of the ADType specified that we can fit into this
//   AED.
UINT32
getMaxNumItemsInAED( LogExtent& logExtent, 
                     ADType adType)
{
    AllocExtentDesc aed(0, adType);

    return aed.maxADTEnts(logExtent.blockSize(), adType);
}

//------------------------------
// n u m B y t e s F o r A D ( )
// Return the number of bytes required to store on AD or embedded
//   data of the ADType specified.
UINT32
numBytesForAD(ADType adType)
{
    UINT32 bytes = 0;

    ShortAllocDesc    sad(0);
    LongAllocDesc     lad(0);
    ExtendedAllocDesc ead(0);

    switch (adType)
    {
    case ad_Embedded:
        bytes = 1;  // Just store one byte.
        break;
    case ad_Short:
        bytes = sad.field.len();
        break;
    case ad_Long:
        bytes = lad.field.len();
        break;
    case ad_Extended:
        bytes = ead.field.len();
        break;
    default:
        PRINT_ERROR("Bad adType in numBytesForAD().");
        break;
    }
    return bytes;
}

//---------------------------------------------------------------------
// Confirm the interface is robust and functioning for this ADType,
//   this number of ADs or embedded data, and this unique id.
// NOTE:  We should add extensive testing of extent lists in here!
TestStatus
confirmInterfaceCase(SpaceResource& space, 
                     LogExtent& logExtent,
                     UINT64 uniqueID,
                     UINT32 numEABytes,
                     ADType adType, 
                     UINT32 dataLength,
                     UINT32 numBodyADs,
                     UINT32 numTailADs)
{
    // We want to instantiate a node, and use it to test the
    //   state changes at the interface level, as well as confirming
    //   state consistency on to/from disk operations.

    // Instantiate our test nodes.
    Node node(space.logVolume());
    Node origNode(space.logVolume());
    Node readBackNode(space.logVolume());

    TestStatus status = TEST_FAIL;
    Error err;

    // Populate a node with embedded data, which we know will fit.
    err = populateNode(
        space, 
        node, 
        uniqueID,               // Unique id.
        numEABytes,             // EA Space.
        adType,                 // Embedded data.
        dataLength,
        numBodyADs,
        numTailADs);  
  
    if(!err)
    { // If we were able to populate the node.
        // Now, make another node just like the first.
        origNode = node;

        if(node != origNode)
        { // If we couldn't even make another one like the first.
            errorMsg("Can't even make a Node like the original!");
#ifdef NODE_DEBUG
            printf("node    : %s\n", (CONST_CHAR_PTR) node.getState());
            printf("origNode: %s\n", (CONST_CHAR_PTR) node.getState());
#endif
        }
        else
        {
            // Write it out, and read it back.
            writeNodeAndReadBack(node, readBackNode, space, logExtent, adType, err);
  
            if(err)
            { // If we could not write the node and read it back.
                errorMsg("Could not write node and read it back.");
            }
            else
            { // We could write the node and read it back.
  
                if(node != readBackNode)
                { // We should have been able to do this.
                    errorMsg("The readback node is not like the written node!");
#ifdef NODE_DEBUG
                    printf("node        : %s\n", (CONST_CHAR_PTR) node.getState());
                    printf("readBackNode: %s\n", (CONST_CHAR_PTR) readBackNode.getState());
#endif
                }
                else
                { // We succeeded.
                    printf("Succeeded in confirm!\n");
                    status = TEST_SUCCESS;
                }
            }
        }
    }
  
    if(status != TEST_SUCCESS)
        errorMsg("Failure in confirmInterfaceCase().");
    return status;
}

//---------------------------------------------------------------------
// This function is used to confirm a node is able to successfully
//   go to and from the surface in a read/write.
// TESTS:
//   TEST if the object is in a state violation with itself.
//
// NOTE:  This function MERELY confirms node read/write capability, and
//   nothing else.
//   It does 
//   not confirm the correct workings for the internal functions of the 
//   Node, including read/write correctness, or node comparison with 
//   the operator==().
//   (That is left for interface verification).
TestStatus
testNodeIntegrity(Node& node, LogExtent& logExtent, 
                  ADType adType)
{
    TestStatus status = TEST_SUCCESS;

    // Confirm the state integrity for this object.
    // Verify we have enough space for our embedded data or at least
    //   one AD.
    Error err;
    if(err = node.ISOError(logExtent))
    { // If we do not have enough space for descriptor information,
        //   or we are otherwise in a state violation.
        // This node has too much EA or Embedded data information.
        status = TEST_FAIL;
#ifdef NODE_DEBUG
        printf("Node does not have integrity, descriptorBytesAvailable is %ld (maxFEBytes is %ld, adType is %ld).\n",
               node.descriptorBytesAvailable(logExtent),
               getMaxFileEntryBytes(logExtent, adType),
               (UINT32)adType);
        printf("Node State:  %s.\n", (CONST_CHAR_PTR)node.getState());
        printError(err);
#endif
    }
    // !!! Insert other state tests here.

    return status;
}

//---------------------------------------------------------------------
// This function is used to confirm a node is able to successfully
//   go to and from the surface in a read/write.
// TESTS:
//   TEST that the object can be retain integrity with readng/writing
//
// NOTE:  This function MERELY confirms node read/write capability, and
//   nothing else.
//   It does 
//   not confirm the correct workings for the internal functions of the 
//   Node, including read/write correctness, or node comparison with 
//   the operator==().
//   (That is left for interface verification).
TestStatus
testNodeWriteRead(SpaceResource& space, LogExtent& logExtent, 
                  Node& node, ADType adType)
{
    TestStatus status = TEST_SUCCESS;

    // Confirm that we can read/write it to the surface.
    Node readBackNode(space.logVolume());

    Error err;
    writeNodeAndReadBack(node, readBackNode, space, logExtent, adType, err);

    if(err || (node != readBackNode))
    {  // If we could not write and read this node, we have
        //   a problem.
        errorMsg("Node read is not the same as node written.");
        status = TEST_FAIL;
    }
    return status;
}

//---------------------------------------------------------------------
// Radomly locate an extent on the surface that will be used for the
//   next test operation.
LogExtent*
getNextTestExtent(Partition& partition, 
                  DriveDevice& driveDevice,
                  UINT32 lowBounds,
                  UINT32 highBounds)
{
    // Now, we are ready to make our LogExtent, which is always one
    //   sector in length (for the descriptor).
    // NOTE:  We create a new extent each time.  If we have an old one,
    //   delete that first.
    // RECALL:  Static variables are set to zero (NULL).
    static LogExtent* extent;
  
    delete extent;  // Get rid of last time's extent.
    UINT32 start = getRandomSector(lowBounds, highBounds);
    extent = new LogExtent(&partition, start,
                           driveDevice.sectorSize());
    extent->extentType(Recorded);

#ifdef NODE_DEBUG
    printf("Making extent, start sector: %ld, size: %ld\n", start,
           driveDevice.sectorSize());
#endif

    if(extent->error())
    { // If our created extent is in an error state.
        errorMsg("Could not create the next extent!");
        printError(extent->error());
    }

    return extent;
}

//---------------------------------------------------------------------
// Return a random (sector) number between the two bounds passed in.
UINT32
getRandomSector(UINT32 lowBounds,
                UINT32 highBounds)
{
    return ((highBounds-lowBounds)/2) + lowBounds;
}

//---------------------------------------------------------------------
// Write this node out and then read it back in.
// NOTE:  If the node is in violation of state integrity (the 
//   node cannot be written), then the surface should not be
//   touched.
// Return an Error value if the write or read failed.
// NOTE:  We don't check to see if the read node is the same as the
//   written node!  We just write it and read it!
void
writeNodeAndReadBack(Node& node,
                     Node& readBackNode,
                     SpaceResource& space,
                     LogExtent& ext,
                     ADType adType, 
                     Error& err)
{
    err = error_None;
 
#ifdef NODE_DEBUG
    printf("Writing to sector:  %ld\n", ext.startBlock());
#endif

    // Write the node out.
    node.toDisk(ext, space, err);

    if(err)
    { // If we could not write the node to disk.
        errorMsg("Could not write the node to disk!");
        printError(err);
    }
    else
    { // We were able to write the node, so we can try to read it back.

#ifdef NODE_DEBUG
        printf("Reading from sector:  %ld\n", ext.startBlock());
#endif

        readBackNode.fromDisk(ext, err);
        if(err)
        { // If we could not read the node back.
            errorMsg("Could not read the node from the disk!");
            printError(err);
        }
    }
    // At this point, err is set and the node is read and written (if
    //   possible).
}

//---------------------------------------------------------------------
Error
populateNode(SpaceResource& space,
             Node& node, 
             UINT64 uniqueID,
             UINT32 numEABytes,
             ADType type, 
             UINT32 dataLength, 
             UINT32 numBodyADs,
             UINT32 numTailADs)
{
    Error err = error_None;

    if(space.error())
    { // If our space resource is in error state.

#ifdef NODE_DEBUG
        errorMsg("Space resource is in error as parameter to populateNode()!");
#endif

        err = NSR::error_Bad_Parameter_Space_Resource;
    }
    else
    { // Input parameters are ok.
        node.clear();  // Erase the old state.

        node.adType(type);
        node.uniqueID(uniqueID);
  
        // Populate the EAs with the number specified.
        // NOTE:  THIS SHOULD FAIL IF NOT A FOUR-BYTE-BOUNDARY!
        //  (but we don't test that now--that's part of the interface testing).
        ByteArray data(getByteArray(numEABytes));
        node.embeddedEASpace() = data;
  
        if(!err) {
            if(type == ad_Embedded)
            { // If we have embedded data, populate that.
                ByteArray data(getByteArray(dataLength));
    
                // Set this embedded data.
                node.embeddedData() = data;
            }
            else // if(type == ad_Short ||
                //    type == ad_Long  ||
                //    type == ad_Embedded)
            { // We have ADs.
                // Create extent lists for the node body and node tail.
    
                err = setLogExtentList(space, node.dataEL(), numBodyADs);
    
                // Now, set the tail LogExtentList.
                if(!err)
                { // If we could set the body extent list.
                    LogExtentList reserveList(space.logVolume());
                    err = setLogExtentList(space, reserveList, numTailADs);
                    if (!err) {
                        node.reserveLELSR().putLogExtentList(reserveList,err);
                    }
                }
            }
        }
    }
  
#ifdef NODE_DEBUG
    if(err)
    { // Problems populating.
        errorMsg("Could not populate the Node!");
        printf("uniqueID: %ld, type: %ld, datalength: %ld, numBodyADs: %ld, numTailADs: %ld.\n",
               uniqueID, type, dataLength, numBodyADs, numTailADs);
    }
    else
    { // All ok.
        printf("After Node Is Populated:  %s\n", (CONST_CHAR_PTR) node.getState());
    }
#endif
    return err;
}

// This function erases the LogExtentList passed in and populates it
//   with new extents, the number of which specified by the parameter
//   passed in.
// NOTE:  Each extent returned will automatically refer to its own
//   location on the surface, because we continuously ask for new
//   space from the space resource without freeing the old space.
// NOTE:  We assume that (since we don't free space back to the space
//   resource) the memory needs for all tests is less than the surface
//   provides.  (We assume we won't fill the disk with these tests).
Error
setLogExtentList(SpaceResource& space, 
                 LogExtentList& logExtentList,
                 UINT32 numADs)
{
    Error err = error_None;

    if(space.error())
    { // If we have have a problem with our space resource.
        err = NSR::error_Bad_Parameter_Space_Resource;
    }
    else
    { // Parameters are Ok.

        // First, erase any old extents we may have had.
        logExtentList.clear();
  
#if 0  // HEY!  WE DON'T NEED REAL EXTENTS!  USE IMAGINARY!
// This works...
        // Get one big extent, then cut it up for all the little
        //   extents for our list.
        LogExtent tempExtent;
#ifdef NODE_DEBUG
        printf("...taking extent length: %ld (%ld available).\n", 
               numADs * space.blockSize(), space.availBytes() );
        fflush(stdout);
#endif
        space.takeLogExtent(tempExtent, numADs * space.blockSize(), err);

        if(err)
        { // If we had a problem getting the extent.
#ifdef NODE_DEBUG
            printf("Could not take the new extent! size: %ld, error: [%ld], %s\n", 
                   numADs * space.blockSize(), (UINT32)err, err.message());
#endif
        }
        else
        { // We got our new extent.
#ifdef NODE_DEBUG
            printf("Got HUGE extent.  Startblock: %ld.\n", 
                   tempExtent.startBlock());
#endif
            // Put the big extent into a temporary list.
            LogExtentList tempList, tempList2;;
            tempList.append(tempExtent,err);

            // Grab the first block (head) of this list for each new
            //   extent.
            for(UINT32 i = 0; (i < numADs) && (!err); i++)
            { // For each new extent.
      
                // Cut off the blocks before the block we want.
                tempList2.TailOf(tempList, i * space.blockSize()+1, err);
#ifdef NODE_DEBUG
                printf("After tail, extent is length: %ld\n", tempList2.infoLen());
#endif

                if(!err)
                { // If we could take the tail of the list.

                    // Cut off the blocks after the block we want.
                    // NOTE:  At this point, we always want the first block in the list.
                    tempList2.HeadOf(tempList2, space.blockSize(), err);

                    if(!err)
                    { // If we could take the head.
                        // Append this list to the meta data list (it is only one block).

#ifdef NODE_DEBUG
                        printf("Trying to concatenate templist2, %ld extents, infolen %ld.\n", tempList2.numLogExtents(), tempList2.infoLen());
                        tempList2.at(0,err)->print(2, "NEW EXTENT:");
#endif
                        logExtentList.concat(tempList2, err);
#ifdef NODE_DEBUG
                        if(err)
                        { // Problem concatenating.
                            printf("Could not concatenate to logExtentList! %ld, %s\n", (UINT32)err, err.message());
                        }
                        else
                        { // We got the extent.
                            printf("We took extent %ld of %ld. ", i, numADs);
                            tempList2.at(0,err)->print(2, "NEW EXTENT:");
                            printf("\v");
                        }
#endif
                    }
#ifdef NODE_DEBUG
                    else
                    { // Problem taking the head.
                        printf("Could not take the head!  Offset: %ld\n",i*space.blockSize());
                    }
#endif
                }
#ifdef NODE_DEBUG
                else
                { // Problem taking the tail.
                    printf("Could not take the tail!  Offset: %ld\n", i*space.blockSize());
                }
#endif
            }
        }
#else // #if 0 because we don't need this.
  
        for(UINT32 i = 0; (i < numADs) && (!err); i++)
        { // For each extent we want.
            LogExtent extent(global_imaginary_partition, space.blockSize()); // An imaginary extent.

            logExtentList.append(extent, err);
        }
 
        if(err)
        { // If we could not append this extent.
            errorMsg("Could not append extent!");
        }      
#endif // #if 0
    }

    if(logExtentList.numExtents() != numADs)
    { // If we had a problem populating.
        // !!! WHAT IS THE CORRECT ERROR for no more space?  (take failed).
        err = NSR::error_UnallocatedSpaceEntry_full;
#ifdef NODE_DEBUG
        errorMsg("Could not populate extent list in setExtentList()!");
#endif
    }
#ifdef NODE_DEBUG
    else
    { // Ok.
        printf("\nPopulated an extent list of %ld extents, infoLen %ld.\n", 
               logExtentList.numLogExtents(), logExtentList.infoLen());
    }
#endif

    return err;
}

//---------------------------------------------------------------------
// Return a byte array with unique populated data of the length 
//   specified.
ByteArray
getByteArray(UINT32 dataLength)
{
    BYTE_PTR bytes = new BYTE[dataLength];

    // Now, set the unique data to this array.
    for(UINT32 i =0; i< dataLength; i++)
    { // For each byte in this array.
        // Set the byte to be a unique number out of 255, in order.
        // Start with 'A' for '0'.
        bytes[i] = (i % 255) + 'A'; 
    }
    ByteArray byteArray(bytes, dataLength);

    // Return a copy of this byte array.
    return byteArray;
}

//----------------------
// c h e c k T e s t ( )
// Check the status parameter passed in.  If not TEST_SUCCESS,
//   then set the returnStatus parameter to TEST_FAILURE and
//   output the error message.  If the parameter is success,
//   output the success message passed in (if there is one).
// NOTE:  We do not modify the returnStatus value unless we
//   identify an error!  It might have already been in an
//   error state, and we don't want to overwrite that! (In
//   case we continue processing even when there is an error).
void
checkTest(TestStatus status, 
          TestStatus& returnStatus,
          CONST_CHAR_PTR errorMessage, 
          CONST_CHAR_PTR successMessage)
{
    if(status == TEST_SUCCESS)
    { // If the test was a success, just output a success message.
        PRINT_MESSAGE(successMessage);
    }
    else
    { // The test was a failure.
        PRINT_ERROR(errorMessage);
        returnStatus = TEST_FAIL;
    }
}

