//-*-c++-*--------------------------------------------------------------
//  dlTest.cpp
//   Tests the DirList object
//
//      Tests DirList::toISO().  Writes random DirLists to & from ISO 
//      and checks that nothing changed in the process.
// 
// Usage: dlTest [devfilename [numLoops]]
//   devfilename defaults to 'tbox'
//   numLoops defaults to 5
//   Note:  If you specify numLoops, you must also specify devfilename
//
//      OUTPUT:
//      Tracks the results of each pass through the toISO/fromISO loop.
//      Gives the DL.entries() sent toISO and the number read fromISO.
//      These should match, and it notes where they don't.  Actually,
//      the equality check also checks that the DirLists' entries are
//      equal to each other.
// 
//      After each pass through the loop, the output shows "ERROR" coming
//      from the fromISO() because we are calling fromISO() on a
//      buffer with garbage after the last entry.  This is what would 
//      happen when you fromISO() the last extent if the last extent
//      isn't completely filled with directory entries.
//
//      See test output for more info on errors.
//
//
//      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.
//
//  CLASSES:
//   ClassName1
//   ClassName2
//  
//----------------------------------------------------------------------
//
// $Source: /projects/HMS/nsr/udfAncil/ssTest/dlTest.cpp,v $
// $Revision: 1.31 $    $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/05/17 15:48:36 $
//

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


// Set max FID length.  Dependent on the standard.
//   !!! Actually, I think maxFidLength = 70 overhead + 256 filename.len
//   That would be 326, but 328 is what I saw as a max length in deTest.
const UINT32 maxFidLength = 328;  

// Set max ByteArray length.  Dependent on ByteArray implementation.  !!!
//   MAX_UINT32 value defined in nsr/nsrLib.h
//   !!! We may implement UINT64 files in a later revision.
const UINT32  maxByteArrayLen = MAX_UINT32; 

const UINT32 maxNumDEs = 500;  // max number DEs per DL in test

// Calc maxDirListISOlen, for testing purposes, to the size of the biggest
//   DirList that we will allow this program to make, based on 
//   maxNumDEs which we set above.
const UINT32 maxDirListISOlen = maxNumDEs * maxFidLength; 

// Set OffsetSpace, space reserved for testing the offset feature of toISO
//   in randomToISO(). 
const UINT32 maxOffsetSpace = 32768; // Arbitrary #

// Calculate real max num DEs per Dir List
//   to about 4 Gig / 400 ~= 4e9/4e2 ~= 1e7 or 10 million directory entries
//   !!! Correct arithmetic, but too big a number for testing.
const UINT32 real_maxNumDEs = maxDirListISOlen / maxFidLength;  

// Calc real maxDirListISOlen, the amount of space in a ByteArray that we can 
//   fill up with a dir list.  
//   !!! Correct arithmetic, but too big a number for testing.
const UINT32 real_maxDirListISOlen = maxByteArrayLen - maxOffsetSpace;

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

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

void makeDE(DirEntry & hiImaDE, LogicalVolume * lvp ) {
    // hidden?
    switch( rand() % 2 ) {
    case 0:  hiImaDE.hidden(False); break;
    case 1:  hiImaDE.hidden(True);  break;
    } // switch

    // deleted?
    switch( rand() % 2 ) {
    case 0:  hiImaDE.deleted(False); break;
    case 1:  hiImaDE.deleted(True);  break;
    } // switch

    // dir entry type
    switch( rand() % 3) {
    case 0:  hiImaDE.entryType(de_Parent); break;
    case 1:  hiImaDE.entryType(de_Subdir); break;
    case 2:  hiImaDE.entryType(de_File);   break;
    } // switch

    // ICB address
    // Make random extent, pull partiton pointer from the DE
    const UINT32 partRefNo = 0;                         // !!! magic #
    Partition * partp = lvp->partitionp(partRefNo); 
    const UINT32 maxLen = 10;                           // !!! magic #
    const UINT32 pLen = lrand48() % maxLen + 1;         // extent length
    // extent length must be + 1 because len = 0 indicates null icb address
    const UINT32 strt =
        (partp->numBlocks() - pLen)
        ? lrand48() % (partp->numBlocks() - pLen)
        : 0;// extent start
    LogExtent randomExtent( partp, strt, pLen );
    randomExtent.markRecorded();        // not terribly needed, but good form
    hiImaDE.setICBAddress( randomExtent );

    // name
    UINT16 len;
    len = rand() % 1000; // Testing name truncation to 255 chars
    BYTE_PTR theName = new BYTE[len];
    for (UINT32 nameFill = 0; nameFill < len; nameFill++) {
        theName[nameFill] = (CHAR) (rand() % (0x7a-0x20) + 0x20);
    }// for
    String nameStr( theName, len );
    hiImaDE.name( nameStr );

    delete [] theName;
} // makeDE

void randomDLToISO( DirList & hiImaDL, ByteArray & buffer, 
                    UINT32 & offset, 
                    LogExtentList & dirEL) {

//  Assumes:  ToISO assumes buffer.length >= dirList.ISOlen()
//  Chooses a random offset into buffer.
 
    UINT32 lenBuf;
    UINT32 lenDL;
    Error err;

    lenDL = hiImaDL.ISOlen();  
    // make a buffer big enough to fit length of dir list + some extra space

    lenBuf = lenDL +  maxOffsetSpace;   
    buffer.clear();
    buffer.setLen( lenBuf );
    offset = (UINT32) (rand() % maxOffsetSpace); // offset into extra space

    hiImaDL.toISO( buffer, offset, dirEL, err);
    if (err)  {
        printf( "ERROR returned from DL.toISO(). \n ");
        printError( err );
    } // if
} // randomDLToISO

///////////////////////////////////////////////////////////////////////////
void testErrDLToISO( DirList & hiImaDL, ByteArray & buffer, 
                    UINT32 & offset, 
                    LogExtentList & dirEL) {

//  Assumes:  ToISO assumes buffer.length >= dirList.ISOlen()
 
    UINT32 lenBuf;
    UINT32 lenDL;
    Error err;

    lenDL = hiImaDL.ISOlen();  

    printf("Do assorted specific-case error testing on DL.toISO()...\n\n");

    // make a buffer just the right size
    printf("make a buffer just the right size\n");
    printf("(NOTE:  This what you should do if you want to write their code to \n");
    printf("expect error_None on return from DL.toISO().  Alternatively, you could\n");
    printf("just ignore the error, or code to check for the expected error.)\n");
    lenBuf = lenDL;   
    buffer.setLen( lenBuf );
    offset = 0; 
    hiImaDL.toISO( buffer, offset, dirEL, err);
    if (err)  {
        printf( "Error returned from DL.toISO(): ");
        printError( err );
        printf( "\n" );
        exit(-1);                       // !!! EXIT LOC HERE !!!
    } // if
    else printf( "Cool.  error_None returned.\n");

    // make buffer too small
    printf( "\nmake buffer too small\n");
    if (lenDL > 10)
        lenBuf = (UINT16) lenDL - 10;  // 10 bytes too small
    else
        lenBuf = 1;
    buffer.setLen( lenBuf );
    offset = 0; 
    hiImaDL.toISO( buffer, offset, dirEL, err);
    if (err)  {
        printf( "Error returned from DL.toISO(): ");
        printError( err );
    } // if
    if (err != NSR::error_End_of_buffer) {
        printf( "Hey!  That wasn't the expected err (End_of_buffer).  Aborting.\n");
        exit(-1);                       // !!! EXIT LOC HERE !!!
    } //if
    else printf( "Cool!  That was the expected error.\n");

    // make offset = buffer
    printf( "\nmake offset == buffer\n");
    lenBuf = (UINT16) lenDL +  (UINT16) maxOffsetSpace;   
    buffer.setLen( lenBuf );
    offset = lenBuf;
    hiImaDL.toISO( buffer, offset, dirEL, err);
    if (err)  {
        printf( "Error returned from DL.toISO(): ");
        printError( err );
    } // if
    if (err != NSR::error_End_of_buffer) {
        printf( "Hey!  That wasn't the expected err (End_of_buffer).  Aborting.\n");
        exit(-1);                       // !!! EXIT LOC HERE !!!
    } //if
    else printf( "Cool!  That was the expected error.\n");
  
    // make offset > buffer
    printf("\nmake offset > buffer\n");
    lenBuf = (UINT16) lenDL +  (UINT16) maxOffsetSpace;   
    buffer.setLen( lenBuf );
    offset = lenBuf + 10;  // 10 bytes bigger
    hiImaDL.toISO( buffer, offset, dirEL, err);
    if (err)  {
        printf( "Error returned from DL.toISO(): ");
        printError( err );
    } // if
    if (err != NSR::error_Index_out_of_bounds) {
        printf( "Hey!  That wasn't the expected err (Index_out_of_bounds).  Aborting.\n");
        exit(-1);                       // !!! EXIT LOC HERE !!!
    } //if
    else printf( "Cool!  That was the expected error.\n");
  
    // make offset + lenDL > buffer
    printf("\nmake offset + lenDL > buffer\n");
    printf("(so you reach end_of_buffer before you finish writing lenDL bytes)\n");
    lenBuf = (UINT16) lenDL +  (UINT16) maxOffsetSpace;   
    buffer.setLen( lenBuf );
    offset = maxOffsetSpace + 10; // 10 bytes too much

    hiImaDL.toISO( buffer, offset, dirEL, err);
    if (err)  {
        printf( "Error returned from DL.toISO(): ");
        printError( err );
    } // if
    if (err != NSR::error_End_of_buffer) {
        printf( "Hey!  That wasn't the expected err (End_of_buffer).  Aborting.\n");
        exit(-1);                       // !!! EXIT LOC HERE !!!
    } //if
    else printf( "Cool!  That was the expected error.\n");

    printf("\nSpecific-case error testing done.\n\n\n");
} // testErrDLToISO

///////////////////////////////////////////////////////////////////////////
void makeDL( DirList & hiImaDL, DirEntry & hiImaDE, LogicalVolume * lvp ) {
    //Make dir list of random length, fill with random DEs
    //Length 0 to maxNumDEs

    UINT32 lenDL = rand() % maxNumDEs; 
    Error err;

    hiImaDL.clear();
    for (INT32 fillDL = 0; fillDL < lenDL; fillDL++) {
        //hiImaDE.clear();  // not needed.
        makeDE( hiImaDE, lvp );
        hiImaDL.append( hiImaDE, err );
        printIfErr( err );
    } // for fillDL
} // makeDL

///////////////////////////////////////////////////////////////////////////
void makeDL( UINT32 min, DirList & hiImaDL, DirEntry & hiImaDE, LogicalVolume * lvp ) {
    //Make dir list of random length, fill with random DEs
    //Length is min to maxNumDEs
    //'min' stands for minimum # of DEs in the DL
    //Assumes 0 <= 'min' < maxNumDEs

    UINT32 lenDL = lrand48() % maxNumDEs;
    if (lenDL < min) 
        lenDL = min;
    Error err;

    hiImaDL.clear();
    for (INT32 fillDL = 0; fillDL < lenDL; fillDL++) {
        //hiImaDE.clear();  // not needed.
        makeDE( hiImaDE, lvp );
        hiImaDL.append( hiImaDE, err );
        printIfErr( err );
    } // for fillDL
} // makeDL

///////////////////////////////////////////////////////////////////////////
//
// Finds the root ICB,
// uses it to quickly set up the logical volume
// Makes random DirLists, fromISO's them, toISO's them back
// and checks that they haven't changed in the process.
//
INT32
main (INT16 argc, CHAR_PTR argv[])
{
    srand(time((time_t *) NULL));
    Error err;  // holds returned errors

    printf( "argc = %d\n",argc);
    
    if ( argc > 3 ||
         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) ) ) {
        printf( "Usage: dlTest [devfilename [numLoops]]\n");
        printf( "  devfilename defaults to 'tbox'\n");
        printf( "  numLoops defaults to 5\n");
        printf( "  Note:  If you specify numLoops, you must also specify devfilename\n" );
        return -1;
    }

    CONST_CHAR_PTR devFile;   // device file name
    if (argc < 2) 
        devFile = "tbox";   // default 
    else
        devFile = argv[1];

    UINT32 maxLoop;    // max toISO/fromISO loops in test
    if (argc < 3) 
        maxLoop = 5;    // max toISO/fromISO loops in test
    else
        maxLoop = atoi( argv[2] );

    DriveDevice ddev(devFile);
    if  (ddev.error())  {
        printf( "Error from drive device constructor...\n" );
        printError( ddev.error() );
        return -1;
    } //if

    SingleSurfaceVolume ssVol( &ddev );
    ssVol.fromDisk(err);
    if (err) {
        printf( "Error from ssVol.fromDisk...\n");
        printError( err );
        return -3;
    } //if

    LogExtent icbExtent = ssVol.rootICBAddr( err );
    if (err) {
        printf( "Error from ssVol.rootICBAddr...\n");
        printError( err );
        return -3;
    } //if

    LogicalVolume * lvp( icbExtent.logVolumep() );
    DirEntry hiImaDE;
    DirList hiImaDL;
    DirList ImasTwin;
    LogExtentList dirEL(*lvp);
    icbExtent.reinit( icbExtent.partitionp(), icbExtent.startBlock(), 
        maxDirListISOlen + maxOffsetSpace, err);
    printIfErr(err);
    dirEL.append(icbExtent, err);
    printIfErr(err);

    ByteArray buffer;
    UINT32 tagLoc;
    UINT32 offset;

    //Test assorted small buffer & big offset errors
    makeDL( 1, hiImaDL, hiImaDE, lvp );  // minumum of 1 DE 


#ifdef DEBUG
String header( "Debugging dlTest.C" );
hiImaDL.print(0, header);
#endif
    testErrDLToISO( hiImaDL, buffer, offset, dirEL);


    //Do actual loop test
    printf( "\n\n"); 
    printf( "Start toISO/fromISO loop test.  \n\n");
    printf( "   Num of entries in first (toISO) and second (fromISO) DLs should match.\n" );
    printf( "DirList::fromISO() returns ERROR because \n" );
    printf( "we are calling it on a buffer with garbage after \n" );
    printf( "the last FID.  The ERROR means, 'I was looking for \n" );
    printf( "the next FID and I found garbage.  I must have \n" );
    printf( "finished reading the directory.'\n");
    printf( "   Please read the docs for DirEntry::fromISO() for an explanation\n");
    printf( "of the various errors and the order in which they will be checked.\n");
    printf( "Note that this test is most likely to return 'error_Bad_tag_location.'\n");
    printf( "That's because the buffer is filled with 0's on construction, so after\n");
    printf( "we read the last real DE in the DL.fromISO(), we try to parse a DE from\n");
    printf( "an area of all 00 bytes.  If you don't set the compile\n");
    printf( "flag NO_TAG_LOCATION_CHECK, an InvalidDescriptor error will be\n");
    printf( "returned from fid.fromISO() when reading all 00s.  \n");
    printf( "   Another less common error is End_of_buffer.  You get this\n");
    printf( "when trying to read a FID from remaining buffer space of \n");
    printf( "a length less than the size of a tag.  A tag is 16 bytes \n");
    printf( "long, so if the last FID you read during a DirList::fromISO()\n");
    printf( "finishes, say, 15 bytes away from the end of the buffer, you get\n");
    printf( "the End_of_buffer error.  You get it because fromISO() tries\n");
    printf( "to read as many FIDs as it can, and when it goes to read the\n");
    printf( "tag of the next FID it finds there is not enough buffer to\n");
    printf( "do so.  Best bet when programming is to set your buffer length\n");
    printf( "to the expected DL.infoLen().  In the more general case,\n");
    printf( "If DL.fromISO() or DE.fromISO() return an error, you need to \n");
    printf( "check to see that you weren't trying to parse a buffer of \n");
    printf( "less than 16 bytes.\n");
    printf( "   To get 'ERROR: error_None' from this test, set buffer.length\n");
    printf( "to DL.infoLen() exactly.\n\n"); 

    for (UINT32 i = 0; i < maxLoop; i++) {
        printf( "Loop %ld \n", i);
        makeDL( hiImaDL, hiImaDE , lvp);
        randomDLToISO( hiImaDL, buffer, offset, dirEL );
        ImasTwin.fromISO( buffer, offset, dirEL, err );

        printf("First DL num Ents = %ld\n", hiImaDL.entries());
        printf("Secnd DL num Ents = %ld\n", ImasTwin.entries());
        printf( "Error returned from DL.fromISO(): ");
        printError( err );

        if (hiImaDL != ImasTwin) {
            printf( "Hey! ERROR: Mismatch. First DL != Secnd DL.\n");
            exit(-1);
        } // if
        else {
            printf( "OK.  First DL equals Secnd DL. \n");
        } // else

        if ((err == NSR::error_Bad_tag_checksum) ||
            (err == NSR::error_Bad_tag_location) ||
            (err == NSR::error_Bad_tag_descriptor_version)) {
            printf( "OK.  Descriptors are expected to be in error when parsing all zeros\n");
            printf( "     and you have not set the NO_TAG_LOCATION_CHECK compile flag.\n");
        } // if
        if ( (err == NSR::error_End_of_buffer) && ( (buffer.length() - ImasTwin.ISOlen()) < 16 ) ) {
            printf( "OK.  End_of_buffer err expected when (buffer.length - dl.ISOlen) < tag.length\n" );
        } // if
        printf("\n");

#ifdef DEBUG2
// This would be a lengthy output.
        printf( "---------------------------------\n");
        String title1( "ToISO DirList" );
        hiImaDL.print( 0, title1, lvp ); 
        String title2( "FromISO DirList" );
        ImasTwin.print( 0, title2, lvp ); 
        printf( "---------------------------------\n");
#endif
    }// for

    printf( "\nOK.  Test finished SUCCESSFULLY.\n");
    return 0;
} // main()  

