/*
 * Copyright (C) 2009 The Android Open Source Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


#ifndef SYSTEM_EXTRAS_TESTS_SDCARD_TESTCASE_H_
#define SYSTEM_EXTRAS_TESTS_SDCARD_TESTCASE_H_

#include <stdlib.h>
#include "stopwatch.h"
#include "sysutil.h"

namespace android_test {

// Class to group test parameters and implementation.
// Takes care of forking child processes and wait for them.

class TestCase {
  public:
    enum Type {UNKNOWN_TEST, WRITE, READ, OPEN_CREATE, READ_WRITE};
    enum Pipe {READ_FROM_CHILD = 0, WRITE_TO_PARENT, READ_FROM_PARENT, WRITE_TO_CHILD};
    enum Sync {NO_SYNC, FSYNC, SYNC};

    // Reads takes less time than writes. This is a basic
    // approximation of how much longer the read tasks must run to
    // terminate roughly at the same time as the write tasks.
    const static int kReadWriteFactor = 5;

    TestCase(const char *appName);

    ~TestCase();

    size_t iter() const { return mIter; }
    void setIter(size_t iter);

    size_t nproc() const { return mNproc; }
    void setNproc(size_t val) { mNproc = val; }

    size_t dataSize() const { return mDataSize; }
    void setDataSize(size_t val) { mDataSize = val; }

    size_t chunkSize() const { return mChunkSize; }
    void setChunkSize(size_t val) { mChunkSize = val; }

    bool newFairSleepers() const { return mNewFairSleepers; }
    void setNewFairSleepers(bool val) {
        mNewFairSleepers = val;
        android::setNewFairSleepers(val);
    }

    bool normalizedSleepers() const { return mNormalizedSleepers; }
    void setNormalizedSleepers(bool val) {
        mNormalizedSleepers = val;
        android::setNormalizedSleepers(val);
    }

    Sync sync() const { return mSync; }
    void setSync(Sync s);
    const char *syncAsStr() const;

    bool cpuScaling() const { return mCpuScaling; }
    void setCpuScaling() { mCpuScaling = true; }

    bool truncateToSize() const { return mTruncateToSize; }
    void setTruncateToSize() { mTruncateToSize = true; }

    int fadvise() { return mFadvice; }
    void setFadvise(const char *advice);
    const char *fadviseAsStr() const;

    // Print the samples.
    void setDump() { StopWatch::setPrintRawMode(true); }

    StopWatch *testTimer() { return mTestTimer; }
    StopWatch *openTimer() { return mOpenTimer; }
    StopWatch *readTimer() { return mReadTimer; }
    StopWatch *writeTimer() { return mWriteTimer; }
    StopWatch *syncTimer() { return mSyncTimer; }
    StopWatch *truncateTimer() { return mTruncateTimer; }

    // Fork the children, run the test and wait for them to complete.
    bool runTest();

    void signalParentAndWait() {
        if (!android::writePidAndWaitForReply(mIpc[WRITE_TO_PARENT], mIpc[READ_FROM_PARENT])) {
            exit(1);
        }
    }

    void createTimers();
    bool setTypeFromName(const char *test_name);
    Type type() const { return mType; }
    pid_t pid() const { return mPid; }
    const char *name() const { return mName; }

    // This is set to the function that will actually do the test when
    // the command line arguments have been parsed. The function will
    // be run in one or more child(ren) process(es).
    bool (*mTestBody)(TestCase *);
private:
    const char *mAppName;
    size_t mDataSize;
    size_t mChunkSize;
    size_t mIter;
    size_t mNproc;
    pid_t mPid;
    char mName[80];
    Type mType;

    bool mDump;  // print the raw values instead of a human friendly report.
    bool mCpuScaling;  // true, do not turn off cpu scaling.
    Sync mSync;
    int mFadvice;
    // When new files are created, truncate them to the final size.
    bool mTruncateToSize;

    bool mNewFairSleepers;
    bool mNormalizedSleepers;

    // IPC
    //        Parent               Child(ren)
    // ---------------------------------------
    // 0: read from child          closed
    // 1: closed                   write to parent
    // 2: closed                   read from parent
    // 3: write to child           closed
    int mIpc[4];

    StopWatch *mTestTimer;  // Used to time the test overall.
    StopWatch *mOpenTimer;  // Used to time the open calls.
    StopWatch *mReadTimer;  // Used to time the read calls.
    StopWatch *mWriteTimer;  // Used to time the write calls.
    StopWatch *mSyncTimer;  // Used to time the sync/fsync calls.
    StopWatch *mTruncateTimer;  // Used to time the ftruncate calls.
};

}  // namespace android_test

#endif  // SYSTEM_EXTRAS_TESTS_SDCARD_TESTCASE_H_