#include <gtest/gtest.h>

#include <unistd.h>
#include <signal.h>
#include <cstdlib>

#include <sstream>
#include <string>
#include <sys/xattr.h>

using namespace std;

const int kRepeatNumber = 100;

static const int kStartDelayTime = 150; // in milliseconds
static const string kClientBinaryName = "canary";

static const string kSignerBinaryName = "pa_hmac_signer";
static const string kTestFileToSign = "/vendor/bin/canary_only_five";
static const string kPaXattrName = "user.pa";

static int RunCommand(const string& cmd, bool is_debug=false) {
  if (is_debug) {
    cout << "Running cmd: '" << cmd << "'" << endl;
  }

  string run_cmd = cmd;
  if (!is_debug) {
    run_cmd += " &> /dev/null";
  }

  return system(run_cmd.c_str());
}

static void RunAppTestCommand(const string& app_name, const string& command="") {
  for (int run = 1; run <= kRepeatNumber; ++run) {
    int pid = fork();

    if (pid == 0) { // Child process
      fclose(stdout);
      if (command.empty()) {
        execlp(app_name.c_str(), app_name.c_str(), (char *)NULL);
      } else {
        execlp(app_name.c_str(), app_name.c_str(), command.c_str(), (char *)NULL);
      }
    } else if (pid > 0) { // Parent process
      int delay_time = kStartDelayTime + (run * 5) % 100;
      usleep(delay_time * 1000);
      kill(pid, SIGTERM);
      wait(NULL);
      removexattr(kTestFileToSign.c_str(), kPaXattrName.c_str());
    } else {
      FAIL() << "Couldn't fork process";
    }
  }
}

class DeadTest : public ::testing::TestWithParam<string> {
  // You can implement all the usual fixture class members here.
  // To access the test parameter, call GetParam() from class
  // TestWithParam<T>.
};

class DeadNewCertificateTest : public ::testing::TestWithParam<string> {
  // You can implement all the usual fixture class members here.
  // To access the test parameter, call GetParam() from class
  // TestWithParam<T>.
};

TEST_P(DeadTest, Test) {
  RunAppTestCommand(kClientBinaryName, GetParam());

  // QC workaround: during first run after canary kill TA might not load
  for (int i = 0; i < 3; ++i) {
    RunCommand(kClientBinaryName + " " + GetParam());
  }

  // Check if system works fine
  int res = RunCommand(kClientBinaryName + " " + GetParam());
  ASSERT_EQ(0, res) << "System should work correctly after command to "
                       "dead applications";
}

INSTANTIATE_TEST_CASE_P(Test,
                        DeadTest,
                        ::testing::Values("", "read", "write"));

TEST_P(DeadNewCertificateTest, Test) {
  RunAppTestCommand(kSignerBinaryName, GetParam());

  // QC workaround: during first run after canary kill TA might not load
  for (int i = 0; i < 3; ++i) {
    RunCommand(kClientBinaryName + " " + GetParam());
  }

  /*
   * Check that signer binary still works fine after the stress testing.
   * So pa_daemon and PaNewCertificate API works well after stress testing.
   */
  int res = RunCommand(kSignerBinaryName + " " + GetParam());
  ASSERT_EQ(0, res) << "Signer binary should works correctly after commands to "
                       "dead applications";
}

INSTANTIATE_TEST_CASE_P(Test,
                        DeadNewCertificateTest,
                        ::testing::Values(kTestFileToSign));
