// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/task.h"
#include "base/task_queue.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

// Sets bools according to whether Run or the destructor were called.
class TrackCallsTask : public Task {
 public:
  TrackCallsTask(bool* ran, bool* deleted)
      : ran_(ran),
        deleted_(deleted) {
    *ran_ = false;
    *deleted_ = false;
  }

  virtual ~TrackCallsTask() {
    *deleted_ = true;
  }

  virtual void Run() {
    *ran_ = true;
  }

 private:
  bool* ran_;
  bool* deleted_;

  DISALLOW_COPY_AND_ASSIGN(TrackCallsTask);
};

// Adds a given task to the queue when run.
class TaskQueuerTask : public Task {
 public:
  TaskQueuerTask(TaskQueue* queue, Task* task_to_queue)
      : queue_(queue),
        task_to_queue_(task_to_queue) {
  }

  virtual void Run() {
    queue_->Push(task_to_queue_);
  }

 private:
  TaskQueue* queue_;
  Task* task_to_queue_;

  DISALLOW_COPY_AND_ASSIGN(TaskQueuerTask);
};

}  // namespace

TEST(TaskQueueTest, RunNoTasks) {
  TaskQueue queue;
  EXPECT_TRUE(queue.IsEmpty());

  queue.Run();
  EXPECT_TRUE(queue.IsEmpty());
}

TEST(TaskQueueTest, RunTasks) {
  TaskQueue queue;

  bool ran_task1 = false;
  bool deleted_task1 = false;
  queue.Push(new TrackCallsTask(&ran_task1, &deleted_task1));

  bool ran_task2 = false;
  bool deleted_task2 = false;
  queue.Push(new TrackCallsTask(&ran_task2, &deleted_task2));

  queue.Run();

  EXPECT_TRUE(ran_task1);
  EXPECT_TRUE(deleted_task1);
  EXPECT_TRUE(ran_task2);
  EXPECT_TRUE(deleted_task2);
  EXPECT_TRUE(queue.IsEmpty());
}

TEST(TaskQueueTest, ClearTasks) {
  TaskQueue queue;

  bool ran_task1 = false;
  bool deleted_task1 = false;
  queue.Push(new TrackCallsTask(&ran_task1, &deleted_task1));

  bool ran_task2 = false;
  bool deleted_task2 = false;
  queue.Push(new TrackCallsTask(&ran_task2, &deleted_task2));

  queue.Clear();

  EXPECT_TRUE(queue.IsEmpty());

  queue.Run();

  EXPECT_FALSE(ran_task1);
  EXPECT_TRUE(deleted_task1);
  EXPECT_FALSE(ran_task2);
  EXPECT_TRUE(deleted_task2);
  EXPECT_TRUE(queue.IsEmpty());
}

TEST(TaskQueueTest, OneTaskQueuesMore) {
  TaskQueue main_queue;

  // Build a task which will queue two more when run.
  scoped_ptr<TaskQueue> nested_queue(new TaskQueue());
  bool ran_task1 = false;
  bool deleted_task1 = false;
  nested_queue->Push(
      new TaskQueuerTask(&main_queue,
                         new TrackCallsTask(&ran_task1, &deleted_task1)));
  bool ran_task2 = false;
  bool deleted_task2 = false;
  nested_queue->Push(
      new TaskQueuerTask(&main_queue,
                         new TrackCallsTask(&ran_task2, &deleted_task2)));

  main_queue.Push(nested_queue.release());

  // Run the task which pushes two more tasks.
  main_queue.Run();

  // None of the pushed tasks shoudl have run yet.
  EXPECT_FALSE(ran_task1);
  EXPECT_FALSE(deleted_task1);
  EXPECT_FALSE(ran_task2);
  EXPECT_FALSE(deleted_task2);
  EXPECT_FALSE(main_queue.IsEmpty());

  // Now run the nested tasks.
  main_queue.Run();

  EXPECT_TRUE(ran_task1);
  EXPECT_TRUE(deleted_task1);
  EXPECT_TRUE(ran_task2);
  EXPECT_TRUE(deleted_task2);
  EXPECT_TRUE(main_queue.IsEmpty());
}
