#include "TestShutdown.h"

namespace mozilla {
namespace _ipdltest {

//-----------------------------------------------------------------------------
// Parent side
void TestShutdownParent::Main() {
  if (!SendStart()) fail("sending Start()");
}

void TestShutdownParent::ActorDestroy(ActorDestroyReason why) {
  if (AbnormalShutdown != why) fail("should have ended test with crash!");

  passed("ok");

  QuitParent();
}

void TestShutdownSubParent::ActorDestroy(ActorDestroyReason why) {
  if (Manager()->ManagedPTestShutdownSubParent().Count() == 0)
    fail("manager should still have managees!");

  if (mExpectCrash && AbnormalShutdown != why)
    fail("expected crash!");
  else if (!mExpectCrash && AbnormalShutdown == why)
    fail("wasn't expecting crash!");

  if (mExpectCrash && 0 == ManagedPTestShutdownSubsubParent().Count())
    fail("expected to *still* have kids");
}

void TestShutdownSubsubParent::ActorDestroy(ActorDestroyReason why) {
  if (Manager()->ManagedPTestShutdownSubsubParent().Count() == 0)
    fail("manager should still have managees!");

  if (mExpectParentDeleted && AncestorDeletion != why)
    fail("expected ParentDeleted == why");
  else if (!mExpectParentDeleted && AncestorDeletion == why)
    fail("wasn't expecting parent delete");
}

//-----------------------------------------------------------------------------
// Child side

mozilla::ipc::IPCResult TestShutdownChild::RecvStart() {
  // test 1: alloc some actors and subactors, delete in
  // managee-before-manager order
  {
    bool expectCrash = false, expectParentDeleted = false;

    PTestShutdownSubChild* c1 = SendPTestShutdownSubConstructor(expectCrash);
    if (!c1) fail("problem sending ctor");

    PTestShutdownSubChild* c2 = SendPTestShutdownSubConstructor(expectCrash);
    if (!c2) fail("problem sending ctor");

    PTestShutdownSubsubChild* c1s1 =
        c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c1s1) fail("problem sending ctor");
    PTestShutdownSubsubChild* c1s2 =
        c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c1s2) fail("problem sending ctor");

    PTestShutdownSubsubChild* c2s1 =
        c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c2s1) fail("problem sending ctor");
    PTestShutdownSubsubChild* c2s2 =
        c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c2s2) fail("problem sending ctor");

    if (!PTestShutdownSubsubChild::Send__delete__(c1s1))
      fail("problem sending dtor");
    if (!PTestShutdownSubsubChild::Send__delete__(c1s2))
      fail("problem sending dtor");
    if (!PTestShutdownSubsubChild::Send__delete__(c2s1))
      fail("problem sending dtor");
    if (!PTestShutdownSubsubChild::Send__delete__(c2s2))
      fail("problem sending dtor");

    if (!c1->CallStackFrame()) fail("problem creating dummy stack frame");
    if (!c2->CallStackFrame()) fail("problem creating dummy stack frame");
  }

  // test 2: alloc some actors and subactors, delete managers first
  {
    bool expectCrash = false, expectParentDeleted = true;

    PTestShutdownSubChild* c1 = SendPTestShutdownSubConstructor(expectCrash);
    if (!c1) fail("problem sending ctor");

    PTestShutdownSubChild* c2 = SendPTestShutdownSubConstructor(expectCrash);
    if (!c2) fail("problem sending ctor");

    PTestShutdownSubsubChild* c1s1 =
        c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c1s1) fail("problem sending ctor");
    PTestShutdownSubsubChild* c1s2 =
        c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c1s2) fail("problem sending ctor");

    PTestShutdownSubsubChild* c2s1 =
        c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c2s1) fail("problem sending ctor");
    PTestShutdownSubsubChild* c2s2 =
        c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c2s2) fail("problem sending ctor");

    // delete parents without deleting kids
    if (!c1->CallStackFrame()) fail("problem creating dummy stack frame");
    if (!c2->CallStackFrame()) fail("problem creating dummy stack frame");
  }

  // test 3: alloc some actors and subactors, then crash
  {
    bool expectCrash = true, expectParentDeleted = false;

    PTestShutdownSubChild* c1 = SendPTestShutdownSubConstructor(expectCrash);
    if (!c1) fail("problem sending ctor");

    PTestShutdownSubChild* c2 = SendPTestShutdownSubConstructor(expectCrash);
    if (!c2) fail("problem sending ctor");

    PTestShutdownSubsubChild* c1s1 =
        c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c1s1) fail("problem sending ctor");
    PTestShutdownSubsubChild* c1s2 =
        c1->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c1s2) fail("problem sending ctor");

    PTestShutdownSubsubChild* c2s1 =
        c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c2s1) fail("problem sending ctor");
    PTestShutdownSubsubChild* c2s2 =
        c2->SendPTestShutdownSubsubConstructor(expectParentDeleted);
    if (!c2s2) fail("problem sending ctor");

    // make sure the ctors have been processed by the other side;
    // the write end of the socket may temporarily be unwriteable
    if (!SendSync()) fail("can't synchronize with parent");

    // "crash", but without tripping tinderbox assert/abort
    // detectors
    _exit(0);
  }
}

void TestShutdownChild::ActorDestroy(ActorDestroyReason why) {
  fail("hey wait ... we should have crashed!");
}

mozilla::ipc::IPCResult TestShutdownSubChild::AnswerStackFrame() {
  if (!PTestShutdownSubChild::Send__delete__(this))
    fail("problem sending dtor");

  // WATCH OUT!  |this| has just deleted

  return IPC_OK();
}

void TestShutdownSubChild::ActorDestroy(ActorDestroyReason why) {
  if (Manager()->ManagedPTestShutdownSubChild().Count() == 0)
    fail("manager should still have managees!");

  if (mExpectCrash && AbnormalShutdown != why)
    fail("expected crash!");
  else if (!mExpectCrash && AbnormalShutdown == why)
    fail("wasn't expecting crash!");

  if (mExpectCrash && 0 == ManagedPTestShutdownSubsubChild().Count())
    fail("expected to *still* have kids");
}

void TestShutdownSubsubChild::ActorDestroy(ActorDestroyReason why) {
  if (Manager()->ManagedPTestShutdownSubsubChild().Count() == 0)
    fail("manager should still have managees!");

  if (mExpectParentDeleted && AncestorDeletion != why)
    fail("expected ParentDeleted == why");
  else if (!mExpectParentDeleted && AncestorDeletion == why)
    fail("wasn't expecting parent delete");
}

}  // namespace _ipdltest
}  // namespace mozilla
