//-*-c++-*--------------------------------------------------------------
//  lelTest.cpp
//   Test program for testing the NSR LogExtentList and
//   PackedLogExtentList classes.
//
//      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.
//
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfAncil/ssTest/lelTest.cpp,v $
// $Revision: 1.11 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/04/30 23:31:56 $

//-----------------------------------------------------------------------------
//
// This program does not interface with the surface at all.

//-----------------------------------------------------------------------------
//                       T e s t   P l a n 
//
// This test plan is to confirm functionality of the LogExtentList
// and PackedLogExtentList classes.  These classes offer the following
// functionality:
//
//    LogExtentList:  Containment of a series of LogExtents, where each
//      extent has an implied (logical) order in the list.  Thus, the
//      list will NOT re-order any extents placed in the list.  Each
//      extent added to the list must be from the same LogicalVolume
//      (and thus have the same blockSize()).  The list stores a
//      reference to its LogicalVolume (required on construction),
//      and confirms extents added are from this volume (or error on 
//      insert).
//
//    o Confirm add(), insertAt() insert extents to the location specified.
// 
//    o Confirm destructive takeExtent() works on the list (successful 
//      take() operations decrease the space in the list by the correct 
//      block multiple).
//
//      - The following are the (general) takeExtent() test cases:
//        NOTE:  All of the following should be rounded UP to the
//          nearest block multiple, except for zero, which should
//          not take any space.
//        . takeLogExtent(zero)
//        . takeLogExtent(one byte)
//        . takeLogExtent(half of one sector)
//        . takeLogExtent(one sector minus one byte)
//        . takeLogExtent(one sector)
//        . takeLogExtent(one sector plus one byte)
//        . takeLogExtent(one and one-half sectors)
//        . takeLogExtent(two sectors minus one byte)
//        . takeLogExtent(two sectors)
//        . takeLogExtent(two sectors plus one byte)
//        . takeLogExtent(two and one-half sectors)
//        . takeLogExtent(three sectors minus one byte)
//        . takeLogExtent(three sectors)
//        . takeLogExtent(three sectors plus one byte)
//        . takeLogExtent(three and one-half sectors)
//        
//        Should SUCCEED:
//        . takeLogExtent(), list has exactly enough space, and
//          - list has extent exactly the correct size (take it).
//        . takeLogExtent(), list has more than enough space, and
//          - first extent has exactly enough space (take it).
//          - first extent has more than enough space (take from first).
//          - first extent is not large enough, but subsequent extent
//            is exactly large enough (take it).
//          - first extent is not large enough, but subsequent extent
//            is more than large enough (take from subsequent).
//        Should FAIL:
//        . takeLogExtent(), list has no space.
//        . takeLogExtent(), list has less than enough space.
//        . takeLogExtent(), list has exact space as the requested
//          extent, but is divided among two or more extents.
//        . takeLogExtent(), list has more than enough space for
//          the request, but no single extent is large enough.
//
//    o Confirm destructive takeExtentList() works on the list (successful 
//      take() operations decrease the space in the list by the correct 
//      block multiple).
//
//      - The following are the (general) takeExtentList() test cases:
//        NOTE:  All of the following should be rounded UP to the
//          nearest block multiple, except for zero, which should
//          not take any space.
//        . takeLogExtentList(zero)
//        . takeLogExtentList(one byte)
//        . takeLogExtentList(half of one sector)
//        . takeLogExtentList(one sector minus one byte)
//        . takeLogExtentList(one sector)
//        . takeLogExtentList(one sector plus one byte)
//        . takeLogExtentList(one and one-half sectors)
//        . takeLogExtentList(two sectors minus one byte)
//        . takeLogExtentList(two sectors)
//        . takeLogExtentList(two sectors plus one byte)
//        . takeLogExtentList(two and one-half sectors)
//        . takeLogExtentList(three sectors minus one byte)
//        . takeLogExtentList(three sectors)
//        . takeLogExtentList(three sectors plus one byte)
//        . takeLogExtentList(three and one-half sectors)
//        
//        Should SUCCEED:
//        . takeLogExtentList(), list has exactly enough space, and
//          - list has extent exactly the correct size (take it).
//        . takeLogExtentList(), list has exact space as the requested
//          extent, but is divided among two or more extents.
//        . takeLogExtentList(), list has more than enough space, and
//          - first extent has exactly enough space (take it).
//          - first extent has more than enough space (take from first).
//          - first extent is not large enough, but subsequent extent
//            is exactly large enough (take from first and subsequent).
//          - first extent is not large enough, but subsequent extent
//            is more than large enough (take from first and subsequent).
//        Should FAIL:
//        . takeLogExtentList(), list has no space.
//        . takeLogExtentList(), list has less than enough space for
//          the request.
//
//    PackedLogExtentList:  A class derived from LogExtentList, this class
//      does everything LogExtentList does.  However, this class ALSO
//      will actively coalesce extents inserted into the list with
//      neighboring extents already in the list.  HOWEVER, NO EXTENTS
//      WILL BE RE-ORDERED (because the LogExtentList implies a logical
//      order must be maintained).
//
//    o Confirm add(), insertAt() correctly coalesces extents without
//      re-ordering extents in the list.
//
//       Given:  
//           position 0: [startBlock = 123, length = 1 block]
//       Append (should coalesce):
//                       [startBlock = 124, length = 1 block]
//
//       Given:  
//           position 0: [startBlock = 123, length = 2 blocks]
//       Append (should coalesce):
//                       [startBlock = 125, length = 1 block]
//
//       Given:  
//           position 0: [startBlock = 123, length = 1 block]
//           position 1: [startBlock = 125, length = 1 block]
//       Insert at position 1 (should coalesce list to one extent):
//                       [startBlock = 124, length = 1 block]
//
//       Given:  
//           position 0: [startBlock = 123, length = 2 blocks]
//           position 1: [startBlock = 127, length = 2 blocks]
//       Insert at position 1 (should coalesce list to one extent):
//                       [startBlock = 125, length = 2 blocks]
//
//-----------------------------------------------------------------------------

#include "nsrLib.h"
#include "nsrRSSdv.h"

UINT32 testLogExtentList(SpaceResource& space);
UINT32 testPackedLogExtentList(SpaceResource& space);
UINT32 testAppendCoalesce(SpaceResource& space);
UINT32 testInsertCoalesce(SpaceResource& space);

INT32 main (INT16 argc, CHAR_PTR argv[])
{
    // First, parse the arguments-- VERY SIMPLE! - the first is the
    // device file name.

    String      dname;

    // Prepare a String variable for use with the DriveDevice constructor.

    if(argc != 2) 
    {
        dname = "tbox";
        (void) printf("WARNING:  Using default devfile '%s' as argument!\n",
                      (const char *)dname);
    } else {
        dname = String(argv[1]);
    }

    // THE FOLLOWING STATIC DATA IS INSTANTIATED ONLY SO WE CAN
    // INTERACT WITH THE SURFACE.
    
    static DriveDevice driveDevice(dname);
    static PhysicalVolume physVolume(&driveDevice);
    static UINT32 startSector = 255;
    static UINT32 numSectors = physVolume.numSectors() - startSector -2;
    static PhysExtent physExtent(&physVolume, startSector, numSectors);
    static Partition partition(physExtent);
    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(lvol,srSpace_UnerasedOnly);
    if (err = space.error()) {
        printf("ERROR constructing SpaceResource: %s\n",
               err.message());
        return -1;
    }

    // Build a logical extent holding all space in the partition.
    LogExtent allSpace(&partition,0,physExtent.numBytes());
    if (err = allSpace.error()) {
        printf("ERROR constructing LogExtent allSpace: %s\n",
               err.message());
        return -1;
    }

    // Place all space into SpaceResource
    space.putLogExtent(allSpace,err);
    if (err) {
        printf("ERROR placing allSpace into space: %s\n",
               err.message());
        return -1;
    }
    
    // Test the LogExtentList.
    testLogExtentList(space);
    testPackedLogExtentList(space);
}


//----------------------------------------------------------------------
// METHOD: testLogExtentList()                                  --CSB
//      
// Test the LogExtentList class.
//
// ARGS:
//  type   <arg>   IN/OUT  <descr>
//
// RETURNS:
//  <explanation of different values>
// 
// PRE-CONDITIONS:
//  List_preconditions  (E.g., assumes)
// 
// POST-CONDITIONS:
//  List_postconditions (E.g., changes to class data members)
// 
// ERRORS:
//  error_
//  error_
//  error_
// 
// NOTES:
//  <Notes_go_here>
//
//----------------------------------------------------------------------
UINT32 
testLogExtentList(SpaceResource& space)
{
  return 0;
}

//----------------------------------------------------------------------
// METHOD: testPackedLogExtentList()                            --CSB
//      
// Test the LogExtentList class.
//
// ARGS:
//  type   <arg>   IN/OUT  <descr>
//
// RETURNS:
//  <explanation of different values>
// 
// PRE-CONDITIONS:
//  List_preconditions  (E.g., assumes)
// 
// POST-CONDITIONS:
//  List_postconditions (E.g., changes to class data members)
// 
// ERRORS:
//  error_
//  error_
//  error_
// 
// NOTES:
//    o Confirm add(), insertAt() correctly coalesces extents without
//      re-ordering extents in the list.
//
//       Given:  
//           position 0: [startBlock = 123, length = 1 block]
//       Append (should coalesce):
//                       [startBlock = 124, length = 1 block]
//
//       Given:  
//           position 0: [startBlock = 123, length = 2 blocks]
//       Append (should coalesce):
//                       [startBlock = 125, length = 1 block]
//
//       Given:  
//           position 0: [startBlock = 123, length = 1 block]
//           position 1: [startBlock = 125, length = 1 block]
//       Insert at position 1 (should coalesce list to one extent):
//                       [startBlock = 124, length = 1 block]
//
//       Given:  
//           position 0: [startBlock = 123, length = 2 blocks]
//           position 1: [startBlock = 127, length = 2 blocks]
//       Insert at position 1 (should coalesce list to one extent):
//                       [startBlock = 125, length = 2 blocks]
//
//----------------------------------------------------------------------
UINT32 
testPackedLogExtentList(SpaceResource& space)
{
    UINT32 returnVal = 1;  // Error by default.
    if(testAppendCoalesce(space)) {
        printf("ERROR:  Failed testAppendCoalesce()!\n");
    }
    else {
        // If we had success.
#ifdef NODE_DEBUG
        printf("PASSED testAppendCoalesce()!\n");
#endif
        if(testInsertCoalesce(space)) {
            printf("ERROR:  Failed testInsertCoalesce()!\n");
        }
        else {
            // If we had success.
#ifdef NODE_DEBUG
            printf("PASSED testInsertCoalesce()!\n");
#endif
            returnVal = 0; // Success.
        }
    }
    return returnVal;
}


//----------------------------------------------------------------------
// METHOD: testAppendCoalesce()                                 --CSB
//      
// Test the confirm that we can correctly coalesce on an append.
//
// ARGS:
//  type   <arg>   IN/OUT  <descr>
//
// RETURNS:
//  Zero on success, non-zero on failure.
// 
// PRE-CONDITIONS:
//  List_preconditions  (E.g., assumes)
// 
// POST-CONDITIONS:
//  List_postconditions (E.g., changes to class data members)
// 
// ERRORS:
//  error_
//  error_
//  error_
// 
// NOTES:
//  <Notes_go_here>
//
//----------------------------------------------------------------------
UINT32
testAppendCoalesce(SpaceResource& space)
{
    PackedLogExtentList packedLEL(space.logVolume());
    PackedLogExtentList tempLEL(space.logVolume());
    LogExtent twoBlockExtent;
    LogExtent oneBlockExtent;
    Error err;
    UINT32 returnVal=0;  // Success by default.

    // Take a contiguous two-block extent.
    space.takeLogExtent(twoBlockExtent, space.blockSize()*2, err);
    
    if(err) {
        // We could not get our two-block extent.
        printf("ERROR:  We could not get our two-block extent to test!\n");
        returnVal = 1;  // Failure.
    }
    else {
        // Put this extent in our temporary list so we can take
        //   half of it at a time.
        tempLEL.append(twoBlockExtent, err);
    
        // Put half the extent into the other list.
        tempLEL.extractLogExtentFromHead(oneBlockExtent, space.blockSize(), err);
        packedLEL.append(oneBlockExtent, err);

        // Put the other extent in the list (they should coalesce).
        tempLEL.extractLogExtentFromHead(oneBlockExtent, space.blockSize(), err);
        packedLEL.append(oneBlockExtent, err);
        
        if((packedLEL.numLogExtents() != 1) || (tempLEL.numLogExtents() != 0)) {
            // If we did not coalesce.
            printf("->Coalesce failed on append!\n");
            packedLEL.print(0, "->Should be one two-block extent:");
            packedLEL.print(0, "->Should be empty:");
            returnVal = 1;  // Failure.
        }
    }
    // Now, confirm the list will not re-order the extents.
    if(!returnVal) {
        // If we have success so far.
        // Extract the tail from full list.
        packedLEL.extractLogExtentFromTail(oneBlockExtent, space.blockSize(), err);
        tempLEL.append(oneBlockExtent, err);

        // Append the other block, and they should not coalesce (the
        //   order would have to change).
        packedLEL.extractLogExtentFromTail(oneBlockExtent, space.blockSize(), err);
        tempLEL.append(oneBlockExtent, err);

        if((tempLEL.numLogExtents() != 2) || (packedLEL.numLogExtents() != 0)) {
            // If we did not coalesce.
            printf("-->Erroneous Coalesce on append (list re-ordered)!\n");
            tempLEL.print(0, "-->Should be one two-block extent:");
            packedLEL.print(0, "-->Should be empty:");
            returnVal = 1;  // Failure.
        }
    }
    return returnVal;  
}

//----------------------------------------------------------------------
// METHOD: testInsertCoalesce()                                 --CSB
//      
// Test the confirm that we can correctly coalesce on an insert.
//
// ARGS:
//  type   <arg>   IN/OUT  <descr>
//
// RETURNS:
//  Zero on success, non-zero on failure.
// 
// PRE-CONDITIONS:
//  List_preconditions  (E.g., assumes)
// 
// POST-CONDITIONS:
//  List_postconditions (E.g., changes to class data members)
// 
// ERRORS:
//  error_
//  error_
//  error_
// 
// NOTES:
//  <Notes_go_here>
//
//----------------------------------------------------------------------
UINT32
testInsertCoalesce(SpaceResource& space)
{
    PackedLogExtentList packedLEL(space.logVolume());
    PackedLogExtentList tempLEL(space.logVolume());
    LogExtent threeBlockExtent;
    LogExtent oneBlockExtent;
    Error err;
    UINT32 returnVal=0;

    // Take a contiguous three-block extent.
    space.takeLogExtent(threeBlockExtent, space.blockSize()*3, err);
    
    if(err) {
        // We could not get our three-block extent.
        printf("ERROR:  We could not get our three-block extent to test!\n");
        returnVal = 1;  // Failure.
    }
    else {
        // Put this extent in our temporary list so we can take
        //   part of it at a time.
        tempLEL.append(threeBlockExtent, err);

#ifdef NODE_DEBUG
        tempLEL.print(0, "..>Before, should be 3 block extent:");
#endif
    
        // Put a one block extent into the other list.
        tempLEL.extractLogExtentFromHead(oneBlockExtent, space.blockSize(), err);
#ifdef NODE_DEBUG
        tempLEL.print(0, "..>Extract, should be 2 block extent:");
#endif

        packedLEL.append(oneBlockExtent, err);
#ifdef NODE_DEBUG
        packedLEL.print(0, "..>Append, should be 1 block extent:");
#endif

        // Put the bottom block extent in the list.
        tempLEL.extractLogExtentFromTail(oneBlockExtent, space.blockSize(), err);
#ifdef NODE_DEBUG
        tempLEL.print(0, "..>Extract, should be middle 1 block extent:");
#endif

        packedLEL.append(oneBlockExtent, err);
#ifdef NODE_DEBUG
        packedLEL.print(0, "..>Append, should be top,bottom extents:");
#endif
        
#ifdef NODE_DEBUG
        printf("..>The tail extracted is %d bytes (should be one block).\n", 
            oneBlockExtent.infoLen());
#endif
        // Insert the middle block (the list should coalesce to one block).
        tempLEL.extractLogExtentFromHead(oneBlockExtent, space.blockSize(), err);
#ifdef NODE_DEBUG
        tempLEL.print(0, "=>Extract, should be empty:");
#endif

        packedLEL.insertAt(oneBlockExtent, 1, err);
#ifdef NODE_DEBUG
        packedLEL.print(0, "=>Should coalesce to 3-block extent:");
#endif
        
        if((packedLEL.numLogExtents() != 1) || 
           (tempLEL.numLogExtents() != 0) ||
           packedLEL.infoLen() != (space.blockSize()*3)) {
            // If we did not coalesce.
            printf("=>Coalesce failed on append!\n");
            packedLEL.print(0, "=>Should be one three-block extent:");
            tempLEL.print(0, "=>Should be empty:");
            returnVal = 1;  // Failure.
        }
    }
    // Now, confirm the list will not re-order the extents.
    if(!returnVal) {
        // If we have success so far.

#ifdef NODE_DEBUG
        packedLEL.print(4, "_>Before any extracts:");
#endif

        // Extract the tail and insert it into the other list.
        packedLEL.extractLogExtentFromTail(oneBlockExtent, space.blockSize(), err);
#ifdef NODE_DEBUG
        packedLEL.print(4, "_>After one block extract:");
#endif

        tempLEL.append(oneBlockExtent, err);
#ifdef NODE_DEBUG
        tempLEL.print(0, "_>After one block insert:");
#endif

        // Append the tail (should not coalesce).
        packedLEL.extractLogExtentFromTail(oneBlockExtent, space.blockSize(), err);
        tempLEL.append(oneBlockExtent, err);
#ifdef NODE_DEBUG
        tempLEL.print(0, "_>After appending another block:");
#endif

        // Now, insert the third extent into the middle.  It should coalesce 
        //   with the third, but not the first.
        packedLEL.extractLogExtentFromTail(oneBlockExtent, space.blockSize(), err);
        tempLEL.insertAt(oneBlockExtent, 1, err);
#ifdef NODE_DEBUG
        tempLEL.print(0, "_>After insert:");
#endif

        if((tempLEL.numLogExtents() != 2) || // Once coalesce, other not.
           (packedLEL.numLogExtents() != 0) || // All removed.
           (tempLEL.at(0, err)->infoLen() != space.blockSize()) || // Not coalesce.
           (tempLEL.at(1, err)->infoLen() != (space.blockSize() *2))) // Coalesce. 
        {
            printf("_>Hey! Erroneous Coalesce on insert (list re-ordered)!\n");
            tempLEL.print(0, "_>Should be two extents (one block, two-block):");
            packedLEL.print(0, "_>Should be empty:");
            returnVal = 1;  // Failure.
        }
    }
    return returnVal;  
}


