//-*-c++-*--------------------------------------------------------------
//  ntfTest.cpp
//   Node To/From Test
//   Tests Node::toDisk() and fromDisk().  
//
//      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/ntfTest.cpp,v $
// $Revision: 1.24 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/05/22 17:25:02 $
// 
// USAGE: ntfTest  [devfilename [numLoops ['SMALL']]] 
//
// ** WARNING **  This test will destroy all data on devfilename. 
// 
//     devfilename defaults to 'tbox2' 
//     numLoops defaults to 5 
//     A non-empty fourth arg causes small printouts. 
//       (Default is larger printouts). 
//  
//     Note:  If you give a value for an option, you must also give 
//            a value for all preceding options. 
//  
// ntfTest:  Writes random Nodes to & from disk  
// and checks that nothing changed in the process.  Writes to random 
// location on the disk. 
// 
//      Again, !!!WARNING!!!  THIS TEST WILL OVERWRITE ALL DATA ON A DISK.
// 
//      Writes random Nodes to & from disk 
//      and checks that nothing changed in the process.  Writes to random
//      location on the disk.
//
//      !!! NOTE:  that the extent passed into toDisk is 
//      only supposed to be one block long.  it's the ICB extent only.
//      toDisk is supposed to make any AED's if needed.
//      !!! Currently, all we do when we make a random node is we make a 
//      random DataEL.  Later we will add 
//      functionality for the rest of what a node can store.
//      !!! Currently, we assume that Node will only take up one sector
//      on the disk and that no AED's will be necessary.
//      !!! Currently, SpaceResource isn't used .  Class Node hasn't imp'd
//      SRs because it curr. assumes that there's no overflow AED's, therefore
//      toDisk will always take one sector, no more, no less and not changed
//      from fromDisk.  This is true for the case where you read in an 
//      already created Node from the disk.  This is not valid for cases
//      where you have made a new Node and want to write it to disk.
//      !!! It is impossible to fromShortAD and get an ERASED extent.
//      Yea, it is beyond the Power (unless we recode Extent::fromShortAD()
//      to choose ERASED for ext_Allocated_and_Unrecorded instead of UNERASED)
//      to do so.  Therefore, ntfTest.C only sends UNERASED extents 
//      toShortAD.  Users should note that if they toShortAD an 'erased', they
//      will get back 'unerased' on fromShortAD.
//
//      !!! ISSUE for later:  NODE needs to implement space resource. and this
//      test needs to read one using an SSVol.
//      !!! makeRandNode() needs to be fleshed out as the setters are imp'd.
//      
//
//      OUTPUT:
//      Tracks the results of each pass through the to/from loop.
//      Gives the DataEL.entries sent and the number read.
//      These should match, and it notes where they don't.  
//      Actually checks for equality.
// 

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

void printError( Error & errout ) { 
    printf("ERROR: %s \n", (CHAR_PTR) errout.message() ); 
}

void printIfErr( Error & err ) {
    if (err) 
        printError( err );
} // printIfErr

void printAndExitIfErr(CHAR_PTR header,  Error & err ) {
    if (err)  {
        printf( "%s", header);
        printError( err );
        exit(-1);
    } // if
} // printIfErr


LogExtent * 
makeRandExtent(Partition * pp, ADType adType ) {
    // Make random extent, given a partition pointer
    // Extent length will be >= 1.
    // Makes the extent on the heap. 
    // PARAMS:   adType  -- if the adType that this extent will
    //                      be translated to is ShortAD, then it chooses
    //                      from eType (imaginary, recorded, unerased).
    //                      That's because shortADs can't store flags for
    //                      unerased/erased and hardcode (!!!) 
    //                      in Extent::fromShortAD is 'unerased'.
    // Normally chooses from eType of (imag, rec, unerased, or erased).
    // ASSUMES:  User is responsible for deleting the LogExtent eventually
    

    const UINT32 maxBlks = pp->numBlocks();

    // Note:  This algo never makes an Extent with Length = 0.
    const UINT32 blkLen = rand() % maxBlks + 1;  // extent length, in blocks

    // Note:  This allocates lengths in block-multiples.  Therefore
    // the extents it makes always have Unused Length=0, since 
    // unused length is the amount of difference between the infoLen
    // and the next block multiple.

    const UINT32 byteLen = ( blkLen * pp->blockSize());// extent length, bytes
    const UINT32 strt = rand() % (maxBlks - blkLen);// extent start
    
    LogExtent * ext;
    ext = new LogExtent( pp, strt, byteLen );

    const UINT16 etype = rand() % 4;  // 4 ExtentTypes
    switch (etype) {  // SET EXT TYPE
    case 0: 
            // make an imaginary extent.  requires a constructor.
            // We are deleting an array?  WRONG!
            delete [] ext; 
            ext = new LogExtent( pp, byteLen );
            break;   
    case 1: 
            if (adType == ad_Long) 
                ext->markErased();  // !!! Only Long (not Short or Extended)
                                   //     can be 'erased'
            else
                ext->markUnerased();
            break;

    case 2: ext->markUnerased(); break;
    case 3: ext->markRecorded(); break;
    default:
            // make an imaginary extent.  requires a constructor.
            // We are deleting an array?  WRONG!
            delete [] ext; 
            ext = new LogExtent( pp, byteLen );
            break;   
    } // switch

    return ext;
} // makeRandExtent


///////////////////////////////////////////////////////////////////////////
LogExtent * 
makeRand1BlkExtent(Partition * pp ) {
    // Make random extent, given a partition pointer
    // Extent length will be one block long.
    // Makes the extent on the heap. 
    // ASSUMES:  User is responsible for deleting the LogExtent eventually
    // Therefore, assumes you aren't going to call so often you run out
    // of free store.  Okay...this is a hack.

    const UINT32 maxBlks = pp->numBlocks();
    const UINT32 blkLen = 1;  // extent length, in blocks
    const UINT32 byteLen = ( blkLen * pp->blockSize());// extent length, bytes
    const UINT32 strt = rand() % (maxBlks - blkLen);// extent start
    
    LogExtent * ext = new LogExtent( pp, strt, byteLen );

    // ICB extents are ALWAYS RECORDED.
    ext->markRecorded();
    return ext;
} // makeRand1BlkExtent

///////////////////////////////////////////////////////////////////////////
void
makeRandEL( LogExtentList & randEL, Partition *  pp, 
                 UINT32 maxNumExts, ADType adType, Error & err) {
    //Make ext list of random length, fill with random exts
    //Length varies from 1 to (maxNumEnts-1)
    //Note:  These extents will be overlapping, unlike in a real
    //       extent list.

    UINT32 lenEL = (lrand48() % (maxNumExts-1)) + 1;
    LogExtent * ep; //extent pointer

    randEL.clear();
    for (UINT32 fillEL = 0; fillEL < lenEL; fillEL++) {
        ep = makeRandExtent( pp, adType );
        randEL.append( *ep, err );
        if (err) {  // only possible error is no_free_store
            return;
        } // if
    } // for 
} // makeRandEL

///////////////////////////////////////////////////////////////////////////
void
makeRandNode( Node & randNode, Partition * pp, ADType adType, Error & err ) { 
    // Make random dummy Node.  Set all the things you can set in a Node to 
    // random values.  Node created is NOT VALID OSTA node because the
    // random extentlists have overlapping extents.
    
    // !!! Right now, all you can set is the DataEL.
    // !!! FUTURE, set the Directory and UniqueID.

    // !!! Note:  ExtentLists are set to dummy EL's that have overlapping
    // extents.  They would not be suitable for a test that seeks to make
    // a real NSR disk by writing valid nodes.

    randNode.clear();

// THINGS TO FILL UP
// FileEntry       fe;
// LogExtentList   dataEList;
// LogExtentList   reserveEList;
// ByteArray       embData;
// EAList          embEAs;

// FileEntry       fe;
// fe will be filled up by var. setters of Node class.

// CHOOSE ONE:
// Data in FE  (embData holds Data, dataElist & reserveEList are empty)
// Data not in FE (embData is empty, dataEList & reserveEList point to data)

// IF DATA NOT IN FE
// Data not in FE--------
// LogExtentList   dataEList;
    //LogExtentList randDataEList;
    Error error;

    // maxNumExts used in makeRandEL
    UINT32 feOverhead = 176; // from ISO std
    UINT32 longADSize = 16;  // from ISO std
    const UINT32 maxNumExts = (pp->blockSize() - feOverhead) / longADSize;  // max number Exts per random EList

    makeRandEL( randNode.dataEL(), pp, maxNumExts, adType, err );
    if (err) 
        return;                         // BAIL OUT if no_free_store

    // We also need to set the unique id.
    randNode.uniqueID(0x00000001);
// LogExtentList   reserveEList;

// ELSE (IF DATA IN FE)
// ByteArray       embData;

// EAList          embEAs;
} // makeRandNode


///////////////////////////////////////////////////////////////////////////
main( INT16 argc, CHAR_PTR argv[] )
{
    srand(time((time_t *) NULL));
    CONST_CHAR_PTR devFile;
    if  ( (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 > 4) 
        ) {
        printf( "USAGE: ntfTest  [devfilename [numLoops ['SMALL']]] \n");
        printf( "\n");
        printf( "** WARNING **  This test will destroy all data on devfilename. \n");
        printf( "\n");
        printf( "    devfilename defaults to 'tbox2' \n");
        printf( "    numLoops defaults to 5 \n");
        printf( "    A non-empty fourth arg causes small printouts. \n");
        printf( "      (Default is larger printouts). \n");
        printf( " \n");
        printf( "    Note:  If you give a value for an option, you must also give \n");
        printf( "           a value for all preceding options. \n");
        printf( " \n");
        printf( "ntfTest:  Writes random Nodes to & from disk  \n");
        printf( "and checks that nothing changed in the process.  Writes to random \n");
        printf( "location on the disk. \n");
        return -1;
    }
    if (argc < 2) 
        devFile = "tbox";  // default
    else
        devFile = argv[1];

    UINT32 numLoops;                // number of to/from loops to do 
    if (argc < 3) 
        numLoops = 5;       // default to 5
    else
        numLoops = atoi( argv[2] );


    Boolean DEBUG_PRINTOUT;
    if (argc < 4) 
        DEBUG_PRINTOUT = True;    // default to large printouts
    else
        DEBUG_PRINTOUT = False;

    // OPEN VOLUME
    ///////////////////////

    Error err;
    DriveDevice ddev(devFile);
    PhysicalVolume pvol(&ddev);

    const UINT32 ASSUMED_PART_START = 257;
    UINT32 start = ASSUMED_PART_START;
    UINT32 numSec = pvol.numSectors() - start - 2;
    PhysExtent pext(&pvol, start, numSec);
    Partition part(pext);
    LogicalVolume lvol(&part);

    // Set logical volume pointer of the partition
    part.logVolumep(&lvol);

    // We need to create a partition and logical volume, because
    // we will be creating extents for translation into
    // longADs or shortADs.  Extent::to/fromLongAD needs a logical volume.
    // Extent::to/fromShortAD needs a partiion.

    //LOOPTEST ON NODE 
    LogExtent icbExtent;        // address to write to
    LogExtentList usedIcbExtents(lvol); // Where retired icbExtents go.
    
    Node sourceNode(lvol);      // gets toDisk'd
    Node destNode(lvol);        // gets fromDisk'd
    // This should use an LELSR instead, since ISOSRs require an
    // instance on the disk.
    LELSpaceResource space(lvol,
                           srSpace_UnerasedOnly);
    
    // Now, give some space to our space resource.
    UINT32 spaceLength = part.blockSize() * (part.numBlocks() - 300);
    printf("Giving space an extent of infoLen %ld starting at block %ld.\n", 
           spaceLength, start);

    LogExtent spaceExtent(&part, start, spaceLength);
    if (err=spaceExtent.error()) {
        printAndExitIfErr( "Could not construct spaceExtent",err);
    }
        
    space.putLogExtent(spaceExtent,err);
    if (err) {
        printAndExitIfErr( "COULD NOT PUT SPACEEXTENT INTO SPACE: ", err);
    }
    ADType adType;              // the type of ADs to write
    Error testErr = error_None;

    // DO TO/FROM DISK LOOP TEST
    for (UINT32 i = 0; i < numLoops; i++) {
        printf( "Loop %ld \n", i);

        // MAKE NODE
        // adType = random ad type
        switch( rand() % 2 ) {
        case 0:  adType = ad_Long; break;
        case 1:  adType = ad_Short;  break;
        default: exit(-1);  // not supposed t' get here.
        } // switch

        makeRandNode(sourceNode, &part, adType, err); // clears prev sourceNode, too    
        printAndExitIfErr( "Error from makeRandNode(): ", err);

        printf( "SOURCE DATA EXTENT LIST: ");
        //static LogExtentList dataEL;
        // NOTE:  We should check testErr.
        printf( "%ld items\n", sourceNode.dataEL().numExtents());
        if (DEBUG_PRINTOUT) {
            String title( "Source DataEL");
#ifdef DEBUG
            sourceNode.printVerbose(UINT32(0), title);
#endif
            printf( "\n" );
        } // if DEBUG

        // WRITE TO DISK
        space.takeLogExtent(icbExtent,part.blockSize(),err);
        printAndExitIfErr( "Error after space.takeLogExtent(): ", err);

        
        // !!! NOTE:  that the extent passed into toDisk is 
        // only supposed to be one block long.  it's the ICB extent only.
        // toDisk is supposed to make any AED's if needed.

        sourceNode.adType(adType);
        sourceNode.toDisk(icbExtent, space, err);
        printAndExitIfErr( "Error after sourceNode.toDisk: ", err);
     
        // READ FROM DISK
        destNode.clear();
        destNode.fromDisk( icbExtent, err );

        printf( "DEST DATA EXTENT LIST: ");

        printf( "%ld items\n", destNode.dataEL().numExtents());
        if (DEBUG_PRINTOUT) {
            String title( "Dest DataEL");
#ifdef DEBUG
            destNode.printVerbose(UINT32(0), title);
#endif
            printf( "\n" );
        } // if DEBUG
        printAndExitIfErr( "Error after destNode.fromDisk: ", err);

        printf( "AD Type:  %ld (0:short, 1:long)\n", (UINT32)adType);
        // CHECK FOR EQUALITY
        if (sourceNode != destNode) {
            printf( "ERROR: After Node.to & fromDisk, Nodes are not equal.\n");
            return(-1);
        } // if
        else 
            printf ("OK.  Source/Dest Nodes are equal.\n");
        printf( "\n");

        usedIcbExtents.append(icbExtent,err);
        printAndExitIfErr( "Error appending icbExtent to usedIcbExtents: ", err);
    } // for numLoops

    space.putLogExtentList(usedIcbExtents,err);
    printAndExitIfErr( "Error putting used ICBExtents back into SR: ", err);
    
    return 0;
} // main()  

