<commit_before>// Copyright (c) 2012 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 <gtest/gtest.h>

#include <string>
#include <sys/wait.h>

#include "base/bind.h"
#include "base/eintr_wrapper.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/process_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "chrome/browser/chromeos/process_proxy/process_proxy_registry.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;

namespace {

const char kTestLineToSend[] = "testline\n";
const char kTestLineExpected[] = "testline\r\n";
const char kCatCommand[] = "cat";
const char kStdoutType[] = "stdout";
const int kTestLineNum = 100;

class TestRunner {
 public:
  virtual ~TestRunner() {}
  virtual void SetupExpectations(pid_t pid) = 0;
  virtual void OnSomeRead(pid_t pid, const std::string& type,
                          const std::string& output) = 0;
  virtual void StartRegistryTest(ProcessProxyRegistry* registry) = 0;

 protected:
  pid_t pid_;
};

class RegistryTestRunner : public TestRunner {
 public:
  virtual ~RegistryTestRunner() {}

  virtual void SetupExpectations(pid_t pid) OVERRIDE {
    pid_ = pid;
    expected_output_.clear();
    left_to_check_index_ = 0;
    // Each line will be echoed twice:
    //   once as a result of terminal default functionality;
    //   once by cat command.
    for (int i = 0; i < kTestLineNum * 2; i++)
      expected_output_.append(kTestLineExpected);
  }

  virtual void OnSomeRead(pid_t pid, const std::string& type,
                          const std::string& output) OVERRIDE {
    EXPECT_EQ(type, kStdoutType);
    EXPECT_EQ(pid_, pid);

    size_t find_result = expected_output_.find(output, left_to_check_index_);
    EXPECT_EQ(left_to_check_index_, find_result);

    size_t new_left_to_check_index = left_to_check_index_ + output.length();
    if (find_result != left_to_check_index_ ||
        new_left_to_check_index >= expected_output_.length()) {
      content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
                                       MessageLoop::QuitClosure());
    } else {
      left_to_check_index_ = new_left_to_check_index;
    }
  }

  virtual void StartRegistryTest(ProcessProxyRegistry* registry) OVERRIDE {
    for (int i = 0; i < kTestLineNum; i++) {
      EXPECT_TRUE(registry->SendInput(pid_, kTestLineToSend));
    }
  }

 private:
  std::string expected_output_;
  size_t left_to_check_index_;
};

class RegistryNotifiedOnProcessExitTestRunner : public TestRunner {
 public:
  virtual ~RegistryNotifiedOnProcessExitTestRunner() {}

  virtual void SetupExpectations(pid_t pid) OVERRIDE {
    output_received_ = false;
    pid_ = pid;
  }

  virtual void OnSomeRead(pid_t pid, const std::string& type,
                          const std::string& output) OVERRIDE {
    EXPECT_EQ(pid_, pid);
    if (!output_received_) {
      output_received_ = true;
      EXPECT_EQ(type, "stdout");
      EXPECT_EQ(output, "p");
      base::KillProcess(pid_, 0 , true);
      return;
    }
    EXPECT_EQ("exit", type);
    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
                                     MessageLoop::QuitClosure());
  }

  virtual void StartRegistryTest(ProcessProxyRegistry* registry) {
    EXPECT_TRUE(registry->SendInput(pid_, "p"));
  }

 private:
  bool output_received_;
};

class SigIntTestRunner : public TestRunner {
 public:
  virtual ~SigIntTestRunner() {}

  virtual void SetupExpectations(pid_t pid) OVERRIDE {
    pid_ = pid;
  }

  virtual void OnSomeRead(pid_t pid, const std::string& type,
                          const std::string& output) OVERRIDE {
    EXPECT_EQ(pid_, pid);
    // We may receive ^C on stdout, but we don't care about that, as long as we
    // eventually received exit event.
    if (type == "exit") {
      content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
                                       MessageLoop::QuitClosure());
    }
 }

  virtual void StartRegistryTest(ProcessProxyRegistry* registry) {
    // Send SingInt and verify the process exited.
    EXPECT_TRUE(registry->SendInput(pid_, "\003"));
  }

 private:
  bool output_received_;
};

}  // namespace

class ProcessProxyTest : public InProcessBrowserTest {
 public:
  ProcessProxyTest() {}
  ~ProcessProxyTest() {}

 protected:
  void InitRegistryTest() {
    registry_ = ProcessProxyRegistry::Get();

    EXPECT_TRUE(registry_->OpenProcess(kCatCommand, &pid_,
        base::Bind(&TestRunner::OnSomeRead,
                   base::Unretained(test_runner_.get()))));

    test_runner_->SetupExpectations(pid_);
    test_runner_->StartRegistryTest(registry_);
  }

  void EndRegistryTest() {
    registry_->CloseProcess(pid_);

    base::TerminationStatus status = base::GetTerminationStatus(pid_, NULL);
    EXPECT_NE(base::TERMINATION_STATUS_STILL_RUNNING, status);
    if (status == base::TERMINATION_STATUS_STILL_RUNNING)
      base::KillProcess(pid_, 0, true);

    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
                                     MessageLoop::QuitClosure());
  }

  void RunTest() {
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
         base::Bind(&ProcessProxyTest::InitRegistryTest,
                    base::Unretained(this)));

    // Wait until all data from output watcher is received (QuitTask will be
    // fired on watcher thread).
    ui_test_utils::RunMessageLoop();

    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
         base::Bind(&ProcessProxyTest::EndRegistryTest,
                    base::Unretained(this)));

    // Wait until we clean up the process proxy.
    ui_test_utils::RunMessageLoop();
  }

  scoped_ptr<TestRunner> test_runner_;

 private:
  ProcessProxyRegistry* registry_;
  pid_t pid_;
};

// Test will open new process that will run cat command, and verify data we
// write to process gets echoed back.
IN_PROC_BROWSER_TEST_F(ProcessProxyTest, RegistryTest) {
  test_runner_.reset(new RegistryTestRunner());
  RunTest();
}

// Open new process, then kill it. Verifiy that we detect when the process dies.
IN_PROC_BROWSER_TEST_F(ProcessProxyTest, RegistryNotifiedOnProcessExit) {
  test_runner_.reset(new RegistryNotifiedOnProcessExitTestRunner());
  RunTest();
}

// Test verifies that \003 message send to process is processed as SigInt.
IN_PROC_BROWSER_TEST_F(ProcessProxyTest, SigInt) {
  test_runner_.reset(new SigIntTestRunner());
  RunTest();
}
<commit_msg>Disabled ProcessProxyTest.SigInt test.<commit_after>// Copyright (c) 2012 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 <gtest/gtest.h>

#include <string>
#include <sys/wait.h>

#include "base/bind.h"
#include "base/eintr_wrapper.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/process_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "chrome/browser/chromeos/process_proxy/process_proxy_registry.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;

namespace {

const char kTestLineToSend[] = "testline\n";
const char kTestLineExpected[] = "testline\r\n";
const char kCatCommand[] = "cat";
const char kStdoutType[] = "stdout";
const int kTestLineNum = 100;

class TestRunner {
 public:
  virtual ~TestRunner() {}
  virtual void SetupExpectations(pid_t pid) = 0;
  virtual void OnSomeRead(pid_t pid, const std::string& type,
                          const std::string& output) = 0;
  virtual void StartRegistryTest(ProcessProxyRegistry* registry) = 0;

 protected:
  pid_t pid_;
};

class RegistryTestRunner : public TestRunner {
 public:
  virtual ~RegistryTestRunner() {}

  virtual void SetupExpectations(pid_t pid) OVERRIDE {
    pid_ = pid;
    expected_output_.clear();
    left_to_check_index_ = 0;
    // Each line will be echoed twice:
    //   once as a result of terminal default functionality;
    //   once by cat command.
    for (int i = 0; i < kTestLineNum * 2; i++)
      expected_output_.append(kTestLineExpected);
  }

  virtual void OnSomeRead(pid_t pid, const std::string& type,
                          const std::string& output) OVERRIDE {
    EXPECT_EQ(type, kStdoutType);
    EXPECT_EQ(pid_, pid);

    size_t find_result = expected_output_.find(output, left_to_check_index_);
    EXPECT_EQ(left_to_check_index_, find_result);

    size_t new_left_to_check_index = left_to_check_index_ + output.length();
    if (find_result != left_to_check_index_ ||
        new_left_to_check_index >= expected_output_.length()) {
      content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
                                       MessageLoop::QuitClosure());
    } else {
      left_to_check_index_ = new_left_to_check_index;
    }
  }

  virtual void StartRegistryTest(ProcessProxyRegistry* registry) OVERRIDE {
    for (int i = 0; i < kTestLineNum; i++) {
      EXPECT_TRUE(registry->SendInput(pid_, kTestLineToSend));
    }
  }

 private:
  std::string expected_output_;
  size_t left_to_check_index_;
};

class RegistryNotifiedOnProcessExitTestRunner : public TestRunner {
 public:
  virtual ~RegistryNotifiedOnProcessExitTestRunner() {}

  virtual void SetupExpectations(pid_t pid) OVERRIDE {
    output_received_ = false;
    pid_ = pid;
  }

  virtual void OnSomeRead(pid_t pid, const std::string& type,
                          const std::string& output) OVERRIDE {
    EXPECT_EQ(pid_, pid);
    if (!output_received_) {
      output_received_ = true;
      EXPECT_EQ(type, "stdout");
      EXPECT_EQ(output, "p");
      base::KillProcess(pid_, 0 , true);
      return;
    }
    EXPECT_EQ("exit", type);
    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
                                     MessageLoop::QuitClosure());
  }

  virtual void StartRegistryTest(ProcessProxyRegistry* registry) {
    EXPECT_TRUE(registry->SendInput(pid_, "p"));
  }

 private:
  bool output_received_;
};

class SigIntTestRunner : public TestRunner {
 public:
  virtual ~SigIntTestRunner() {}

  virtual void SetupExpectations(pid_t pid) OVERRIDE {
    pid_ = pid;
  }

  virtual void OnSomeRead(pid_t pid, const std::string& type,
                          const std::string& output) OVERRIDE {
    EXPECT_EQ(pid_, pid);
    // We may receive ^C on stdout, but we don't care about that, as long as we
    // eventually received exit event.
    if (type == "exit") {
      content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
                                       MessageLoop::QuitClosure());
    }
 }

  virtual void StartRegistryTest(ProcessProxyRegistry* registry) {
    // Send SingInt and verify the process exited.
    EXPECT_TRUE(registry->SendInput(pid_, "\003"));
  }

 private:
  bool output_received_;
};

}  // namespace

class ProcessProxyTest : public InProcessBrowserTest {
 public:
  ProcessProxyTest() {}
  ~ProcessProxyTest() {}

 protected:
  void InitRegistryTest() {
    registry_ = ProcessProxyRegistry::Get();

    EXPECT_TRUE(registry_->OpenProcess(kCatCommand, &pid_,
        base::Bind(&TestRunner::OnSomeRead,
                   base::Unretained(test_runner_.get()))));

    test_runner_->SetupExpectations(pid_);
    test_runner_->StartRegistryTest(registry_);
  }

  void EndRegistryTest() {
    registry_->CloseProcess(pid_);

    base::TerminationStatus status = base::GetTerminationStatus(pid_, NULL);
    EXPECT_NE(base::TERMINATION_STATUS_STILL_RUNNING, status);
    if (status == base::TERMINATION_STATUS_STILL_RUNNING)
      base::KillProcess(pid_, 0, true);

    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
                                     MessageLoop::QuitClosure());
  }

  void RunTest() {
    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
         base::Bind(&ProcessProxyTest::InitRegistryTest,
                    base::Unretained(this)));

    // Wait until all data from output watcher is received (QuitTask will be
    // fired on watcher thread).
    ui_test_utils::RunMessageLoop();

    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
         base::Bind(&ProcessProxyTest::EndRegistryTest,
                    base::Unretained(this)));

    // Wait until we clean up the process proxy.
    ui_test_utils::RunMessageLoop();
  }

  scoped_ptr<TestRunner> test_runner_;

 private:
  ProcessProxyRegistry* registry_;
  pid_t pid_;
};

// Test will open new process that will run cat command, and verify data we
// write to process gets echoed back.
IN_PROC_BROWSER_TEST_F(ProcessProxyTest, RegistryTest) {
  test_runner_.reset(new RegistryTestRunner());
  RunTest();
}

// Open new process, then kill it. Verifiy that we detect when the process dies.
IN_PROC_BROWSER_TEST_F(ProcessProxyTest, RegistryNotifiedOnProcessExit) {
  test_runner_.reset(new RegistryNotifiedOnProcessExitTestRunner());
  RunTest();
}

// Test verifies that \003 message send to process is processed as SigInt.
// Timing out on the waterfall: http://crbug.com/115064
IN_PROC_BROWSER_TEST_F(ProcessProxyTest, DISABLED_SigInt) {
  test_runner_.reset(new SigIntTestRunner());
  RunTest();
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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 <string>

#include "base/file_util.h"
#include "chrome/browser/view_ids.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/test/automation/constrained_window_proxy.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "chrome/views/event.h"
#include "net/base/net_util.h"

const int kRightCloseButtonOffset = 55;
const int kBottomCloseButtonOffset = 20;

class InteractiveConstrainedWindowTest : public UITest {
 protected:
  InteractiveConstrainedWindowTest() {
    show_window_ = true;
  }
};

TEST_F(InteractiveConstrainedWindowTest, TestOpenAndResizeTo) {
  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());

  scoped_ptr<WindowProxy> window(
      automation()->GetWindowForBrowser(browser.get()));
  ASSERT_TRUE(window.get());

  scoped_ptr<TabProxy> tab(browser->GetTab(0));
  ASSERT_TRUE(tab.get());

  std::wstring filename(test_data_directory_);
  file_util::AppendToPath(&filename, L"constrained_files");
  file_util::AppendToPath(&filename,
                          L"constrained_window_onload_resizeto.html");
  ASSERT_TRUE(tab->NavigateToURL(net::FilePathToFileURL(filename)));

  gfx::Rect tab_view_bounds;
  ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER,
                                    &tab_view_bounds, true));

  // Simulate a click of the actual link to force user_gesture to be
  // true; if we don't, the resulting popup will be constrained, which
  // isn't what we want to test.
  POINT link_point(tab_view_bounds.CenterPoint().ToPOINT());
  ASSERT_TRUE(window->SimulateOSClick(link_point,
                                      views::Event::EF_LEFT_BUTTON_DOWN));

  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(2, 1000));

  scoped_ptr<BrowserProxy> popup_browser(automation()->GetBrowserWindow(1));
  ASSERT_TRUE(popup_browser != NULL);
  scoped_ptr<WindowProxy> popup_window(
      automation()->GetWindowForBrowser(popup_browser.get()));
  ASSERT_TRUE(popup_window != NULL);

  // Make sure we were created with the correct width and height.
  gfx::Rect rect;
  bool is_timeout = false;
  ASSERT_TRUE(popup_window->GetViewBoundsWithTimeout(
                  VIEW_ID_TAB_CONTAINER, &rect, false, 1000, &is_timeout));
  ASSERT_FALSE(is_timeout);
  ASSERT_EQ(300, rect.width());
  ASSERT_EQ(320, rect.height());

  // Send a click to the popup window to test resizeTo.
  ASSERT_TRUE(popup_window->GetViewBounds(VIEW_ID_TAB_CONTAINER,
                                          &tab_view_bounds, true));
  POINT popup_link_point(tab_view_bounds.CenterPoint().ToPOINT());
  ASSERT_TRUE(popup_window->SimulateOSClick(
                  popup_link_point, views::Event::EF_LEFT_BUTTON_DOWN));

  // No idea how to wait here other then sleeping. This timeout used to be
  // lower, then we started hitting it before it was done. :(
  Sleep(5000);

  // The actual content will be LESS than (200, 200) because resizeTo
  // deals with a window's outer{Width,Height} instead of its
  // inner{Width,Height}.
  is_timeout = false;
  ASSERT_TRUE(popup_window->GetViewBoundsWithTimeout(
                  VIEW_ID_TAB_CONTAINER, &rect, false, 1000, &is_timeout));
  ASSERT_FALSE(is_timeout);
  ASSERT_LT(rect.width(), 200);
  ASSERT_LT(rect.height(), 200);
}

// Tests that in the window.open() equivalent of a fork bomb, we stop building
// windows.
TEST_F(InteractiveConstrainedWindowTest, DISABLED_DontSpawnEndlessPopups) {
  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());

  scoped_ptr<WindowProxy> window(
      automation()->GetWindowForBrowser(browser.get()));
  ASSERT_TRUE(window.get());

  scoped_ptr<TabProxy> tab(browser->GetTab(0));
  ASSERT_TRUE(tab.get());

  std::wstring filename(test_data_directory_);
  file_util::AppendToPath(&filename, L"constrained_files");
  file_util::AppendToPath(&filename,
                          L"infinite_popups.html");
  ASSERT_TRUE(tab->NavigateToURL(net::FilePathToFileURL(filename)));

  gfx::Rect tab_view_bounds;
  ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER,
                                    &tab_view_bounds, true));

  // Simulate a click of the actual link to force user_gesture to be
  // true; if we don't, the resulting popup will be constrained, which
  // isn't what we want to test.
  POINT link_point(tab_view_bounds.CenterPoint().ToPOINT());
  ASSERT_TRUE(window->SimulateOSClick(link_point,
                                      views::Event::EF_LEFT_BUTTON_DOWN));

  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(2, 1000));

  scoped_ptr<BrowserProxy> popup_browser(automation()->GetBrowserWindow(1));
  ASSERT_TRUE(popup_browser.get());
  scoped_ptr<TabProxy> popup_tab(popup_browser->GetTab(0));
  ASSERT_TRUE(popup_tab.get());

  // And now we spin on this, waiting to make sure that we don't spawn popup
  // windows endlessly. The current limit is 25, so allowing for possible race
  // conditions and one off errors, don't break out until we go over 35 popup
  // windows (in which case we are bork bork bork).
  const int kMaxPopupWindows = 35;
  int constrained_window_count = 0;
  int new_constrained_window_count;
  bool continuing = true;
  while (continuing && constrained_window_count < kMaxPopupWindows) {
    continuing = popup_tab->WaitForChildWindowCountToChange(
        constrained_window_count, &new_constrained_window_count,
        100000);
    EXPECT_GE(new_constrained_window_count, constrained_window_count);
    EXPECT_LE(new_constrained_window_count, kMaxPopupWindows);
    constrained_window_count = new_constrained_window_count;
  }
}
<commit_msg>Updates one of the unit tests I disabled when I rewrote the popup blocker UI. Review URL: http://codereview.chromium.org/10206<commit_after>// Copyright (c) 2006-2008 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 <string>

#include "base/file_util.h"
#include "chrome/browser/view_ids.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/l10n_util.h"
#include "chrome/test/automation/automation_constants.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/constrained_window_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/automation/window_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "chrome/views/event.h"
#include "net/base/net_util.h"

#include "generated_resources.h"

const int kRightCloseButtonOffset = 55;
const int kBottomCloseButtonOffset = 20;

class InteractiveConstrainedWindowTest : public UITest {
 protected:
  InteractiveConstrainedWindowTest() {
    show_window_ = true;
  }
};

TEST_F(InteractiveConstrainedWindowTest, TestOpenAndResizeTo) {
  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());

  scoped_ptr<WindowProxy> window(
      automation()->GetWindowForBrowser(browser.get()));
  ASSERT_TRUE(window.get());

  scoped_ptr<TabProxy> tab(browser->GetTab(0));
  ASSERT_TRUE(tab.get());

  std::wstring filename(test_data_directory_);
  file_util::AppendToPath(&filename, L"constrained_files");
  file_util::AppendToPath(&filename,
                          L"constrained_window_onload_resizeto.html");
  ASSERT_TRUE(tab->NavigateToURL(net::FilePathToFileURL(filename)));

  gfx::Rect tab_view_bounds;
  ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER,
                                    &tab_view_bounds, true));

  // Simulate a click of the actual link to force user_gesture to be
  // true; if we don't, the resulting popup will be constrained, which
  // isn't what we want to test.
  POINT link_point(tab_view_bounds.CenterPoint().ToPOINT());
  ASSERT_TRUE(window->SimulateOSClick(link_point,
                                      views::Event::EF_LEFT_BUTTON_DOWN));

  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(2, 1000));

  scoped_ptr<BrowserProxy> popup_browser(automation()->GetBrowserWindow(1));
  ASSERT_TRUE(popup_browser != NULL);
  scoped_ptr<WindowProxy> popup_window(
      automation()->GetWindowForBrowser(popup_browser.get()));
  ASSERT_TRUE(popup_window != NULL);

  // Make sure we were created with the correct width and height.
  gfx::Rect rect;
  bool is_timeout = false;
  ASSERT_TRUE(popup_window->GetViewBoundsWithTimeout(
                  VIEW_ID_TAB_CONTAINER, &rect, false, 1000, &is_timeout));
  ASSERT_FALSE(is_timeout);
  ASSERT_EQ(300, rect.width());
  ASSERT_EQ(320, rect.height());

  // Send a click to the popup window to test resizeTo.
  ASSERT_TRUE(popup_window->GetViewBounds(VIEW_ID_TAB_CONTAINER,
                                          &tab_view_bounds, true));
  POINT popup_link_point(tab_view_bounds.CenterPoint().ToPOINT());
  ASSERT_TRUE(popup_window->SimulateOSClick(
                  popup_link_point, views::Event::EF_LEFT_BUTTON_DOWN));

  // No idea how to wait here other then sleeping. This timeout used to be
  // lower, then we started hitting it before it was done. :(
  Sleep(5000);

  // The actual content will be LESS than (200, 200) because resizeTo
  // deals with a window's outer{Width,Height} instead of its
  // inner{Width,Height}.
  is_timeout = false;
  ASSERT_TRUE(popup_window->GetViewBoundsWithTimeout(
                  VIEW_ID_TAB_CONTAINER, &rect, false, 1000, &is_timeout));
  ASSERT_FALSE(is_timeout);
  ASSERT_LT(rect.width(), 200);
  ASSERT_LT(rect.height(), 200);
}

// Helper function used to get the number of blocked popups out of the window
// title.
bool ParseCountOutOfTitle(const std::wstring& title, int* output)
{
  // Since we will be reading the number of popup windows open by grabbing the
  // number out of the window title, and that format string is localized, we
  // need to find out the offset into that string.
  const wchar_t* placeholder = L"XXXX";
  size_t offset =
      l10n_util::GetStringF(IDS_POPUPS_BLOCKED_COUNT, placeholder).
      find(placeholder);

  std::wstring number;
  while (offset < title.size() && iswdigit(title[offset])) {
    number += title[offset];
    offset++;
  }

  return StringToInt(number, output);
}

// Tests that in the window.open() equivalent of a fork bomb, we stop building
// windows.
TEST_F(InteractiveConstrainedWindowTest, DontSpawnEndlessPopups) {
  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  ASSERT_TRUE(browser.get());

  scoped_ptr<WindowProxy> window(
      automation()->GetWindowForBrowser(browser.get()));
  ASSERT_TRUE(window.get());

  scoped_ptr<TabProxy> tab(browser->GetTab(0));
  ASSERT_TRUE(tab.get());

  std::wstring filename(test_data_directory_);
  file_util::AppendToPath(&filename, L"constrained_files");
  file_util::AppendToPath(&filename,
                          L"infinite_popups.html");
  ASSERT_TRUE(tab->NavigateToURL(net::FilePathToFileURL(filename)));

  gfx::Rect tab_view_bounds;
  ASSERT_TRUE(window->GetViewBounds(VIEW_ID_TAB_CONTAINER,
                                    &tab_view_bounds, true));

  // Simulate a click of the actual link to force user_gesture to be
  // true; if we don't, the resulting popup will be constrained, which
  // isn't what we want to test.
  POINT link_point(tab_view_bounds.CenterPoint().ToPOINT());
  ASSERT_TRUE(window->SimulateOSClick(link_point,
                                      views::Event::EF_LEFT_BUTTON_DOWN));

  ASSERT_TRUE(automation()->WaitForWindowCountToBecome(2, 1000));

  scoped_ptr<BrowserProxy> popup_browser(automation()->GetBrowserWindow(1));
  ASSERT_TRUE(popup_browser.get());
  scoped_ptr<TabProxy> popup_tab(popup_browser->GetTab(0));
  ASSERT_TRUE(popup_tab.get());

  int constrained_window_count = 0;
  ASSERT_TRUE(popup_tab->WaitForChildWindowCountToChange(
                  0, &constrained_window_count, 10000));
  ASSERT_EQ(1, constrained_window_count);
  scoped_ptr<ConstrainedWindowProxy> constrained_window(
      popup_tab->GetConstrainedWindow(0));
  ASSERT_TRUE(constrained_window.get());

  // And now we spin, waiting to make sure that we don't spawn popup
  // windows endlessly. The current limit is 25, so allowing for possible race
  // conditions and one off errors, don't break out until we go over 30 popup
  // windows (in which case we are bork bork bork).
  const int kMaxPopupWindows = 30;

  int popup_window_count = 0;
  int new_popup_window_count = 0;
  int times_slept = 0;
  bool continuing = true;
  while (continuing && popup_window_count < kMaxPopupWindows) {
    std::wstring title;
    ASSERT_TRUE(constrained_window->GetTitle(&title));
    ASSERT_TRUE(ParseCountOutOfTitle(title, &new_popup_window_count));
    if (new_popup_window_count == popup_window_count) {
      if (times_slept == 10) {
        continuing = false;
      } else {
        // Nothing intereseting is going on wait it out.
        Sleep(automation::kSleepTime);
        times_slept++;
      }
    } else {
      times_slept = 0;
    }

    EXPECT_GE(new_popup_window_count, popup_window_count);
    EXPECT_LE(new_popup_window_count, kMaxPopupWindows);
    popup_window_count = new_popup_window_count;
  }
}
<|endoftext|>


<commit_before>#include "DumbReceive.hpp"
#include "Utils.hpp"
#include <math.h>

namespace Gameplay { REGISTER_CONFIGURABLE(DumbReceive) }

ConfigDouble *Gameplay::DumbReceive::_backoffDistance;
ConfigDouble *Gameplay::DumbReceive::_maxExecutionError;
ConfigDouble *Gameplay::DumbReceive::_kickTimeout;

ConfigBool *Gameplay::DumbReceive::_debug;

void Gameplay::DumbReceive::createConfiguration(Configuration *cfg)
{
	_backoffDistance = new ConfigDouble(cfg, "DumbReceive/Backoff Distance", 0.078674155);
	_maxExecutionError = new ConfigDouble(cfg, "DumbReceive/Max Execution Error", 0.015);
	_kickTimeout = new ConfigDouble(cfg, "DumbReceive/Kick Timeout", 3.0);

	_debug = new ConfigBool(cfg, "StablePivotKick/Debug", true);
}

Gameplay::DumbReceive::DumbReceive(GameplayModule *gameplay)
	: ActionBehavior(gameplay),
	  _state(Setup),
	  _success(false),
	  _kickDelta(0)
{
}

bool Gameplay::DumbReceive::isSettingUp()
{
	return _state == Setup;
}

bool Gameplay::DumbReceive::isSetup()
{
	return isAtReceivePosition() && _state == Setup;
}

bool Gameplay::DumbReceive::isActing()
{
	return _state == Receive_PassKicking || _state == Receive_PassDone;
}

bool Gameplay::DumbReceive::isDone()
{
	return _state == Done;
}

void Gameplay::DumbReceive::restart()
{
	_state = Setup;
	_success = false;
	_kickDelta = 0;
}

Geometry2d::Point Gameplay::DumbReceive::receivePosition()
{
	using namespace Geometry2d;

	Point receiveTarget;  // Point at which to place robot mouth
	Point passVector;     // Direction of pass
	if(_state == Setup) {
		receiveTarget = actionTarget;
		passVector = actionTarget - ball().pos;
	} else if(_state == Receive_PassKicking && partner) {
		OurRobot* passer = partner->robot;
		// The direction passer faces is the passing direction
		passVector = passer->kickerBar().center() - passer->pos;
		Line passLine(ball().pos, ball().pos + passVector);
		// Put mouth on the line
		// Using robot->pos would have caused the receiver to move backwards
		receiveTarget = passLine.nearestPoint(robot->kickerBar().center());
	} else if(_state == Receive_PassDone) {
		Line passLine(ball().pos, ball().pos + ball().vel);
		receiveTarget = passLine.nearestPoint(robot->kickerBar().center());
		passVector = ball().vel;
	} else if(_state == Done) {
		// Return start, if nowhere else
		receiveTarget = actionTarget;
		// passVector is (0,0)
	}

	// Calculate location of robot center
	receiveTarget += passVector **_backoffDistance;

	return receiveTarget;
}

float Gameplay::DumbReceive::receivePositionError()
{
	using namespace Geometry2d;
	float dist = (receivePosition() - robot->pos).mag();
	float distAngle = Line(receivePosition(), ball().pos).distTo(robot->pos);

	return std::max(dist, distAngle);
}

bool Gameplay::DumbReceive::isAtReceivePosition()
{
	return receivePositionError() < *_maxExecutionError;
}

bool Gameplay::DumbReceive::run()
{
	using namespace Geometry2d;

	if(!robot || !robot->visible) {
		return false;
	}

	// State changing
	if(_state == Setup) {
		if(isAtReceivePosition()) {
			if(partner && partner->isActing()) {
				_state = Receive_PassKicking;
			} else if(partner && partner->isDone()) {
				_state = Receive_PassDone;
			}
			// else if(!partner) {
			// 	_state = Receive_PassDone;
			// }
		}
	} else if(_state == Receive_PassKicking) {
		if(partner && partner->isSettingUp()) {
			_state = Setup;
		} else if(partner && partner->isDone()) {
			_state = Receive_PassDone;
		} else if(!partner) {
			_state = Receive_PassDone;
		}
	} else if(_state == Receive_PassDone) {
		if(_kickDelta == 0) {
			_kickDelta = timestamp();
		}

		if(robot->hasBall()) {
			_state = Done;
		} else if(timestamp() - _kickDelta > *_kickTimeout * 1000000) {  // Micro-seconds
			_state = Done;
		}
	} else if(_state == Done) {
		// Do nothing
	}

	// Debug visualization
	if(*_debug) {
		if(_state == Setup) {
			robot->addText(QString("DR: Setup"), Qt::yellow, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::yellow, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::yellow, QString("DumbReceive"));
		} else if(_state == Receive_PassKicking) {
			robot->addText(QString("DR: Receive PassKicking"), Qt::yellow, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::yellow, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::yellow, QString("DumbReceive"));
			// Draw line to show where we think the ball is going to travel
			state()->drawLine(ball().pos, receivePosition(), Qt::yellow, QString("DumbReceive"));
		} else if(_state == Receive_PassDone) {
			robot->addText(QString("DR: Receive PassDone"), Qt::green, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::green, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::green, QString("DumbReceive"));
			// Draw line to show where we think the ball is going to travel
			state()->drawLine(ball().pos, receivePosition(), Qt::green, QString("DumbReceive"));
		} else if(_state == Done) {
			// Do nothing
			robot->addText(QString("DR: Done"), Qt::red, QString("DumbReceive"));
		}
	}

	// Commands
	if(_state == Setup) {
		robot->avoidBall();
		robot->move(receivePosition(), false);
		robot->face(ball().pos);
	} else if(_state == Receive_PassKicking || _state == Receive_PassDone) {
		// FIXME: This has the side effect of always being on (even after play termination)
		// because it modifies an OurRobot internal variable.
		robot->disableAvoidBall();
		robot->move(receivePosition(), false);
		robot->face(ball().pos);
	}

	return true;
}
<commit_msg>fixed segfault in DumbReceive<commit_after>#include "DumbReceive.hpp"
#include "Utils.hpp"
#include <math.h>

namespace Gameplay { REGISTER_CONFIGURABLE(DumbReceive) }

ConfigDouble *Gameplay::DumbReceive::_backoffDistance;
ConfigDouble *Gameplay::DumbReceive::_maxExecutionError;
ConfigDouble *Gameplay::DumbReceive::_kickTimeout;

ConfigBool *Gameplay::DumbReceive::_debug;

void Gameplay::DumbReceive::createConfiguration(Configuration *cfg)
{
	_backoffDistance = new ConfigDouble(cfg, "DumbReceive/Backoff Distance", 0.078674155);
	_maxExecutionError = new ConfigDouble(cfg, "DumbReceive/Max Execution Error", 0.015);
	_kickTimeout = new ConfigDouble(cfg, "DumbReceive/Kick Timeout", 3.0);

	_debug = new ConfigBool(cfg, "StablePivotKick/Debug", true);
}

Gameplay::DumbReceive::DumbReceive(GameplayModule *gameplay)
	: ActionBehavior(gameplay),
	  _state(Setup),
	  _success(false),
	  _kickDelta(0)
{
}

bool Gameplay::DumbReceive::isSettingUp()
{
	return _state == Setup;
}

bool Gameplay::DumbReceive::isSetup()
{
	return isAtReceivePosition() && _state == Setup;
}

bool Gameplay::DumbReceive::isActing()
{
	return _state == Receive_PassKicking || _state == Receive_PassDone;
}

bool Gameplay::DumbReceive::isDone()
{
	return _state == Done;
}

void Gameplay::DumbReceive::restart()
{
	_state = Setup;
	_success = false;
	_kickDelta = 0;
}

Geometry2d::Point Gameplay::DumbReceive::receivePosition()
{
	using namespace Geometry2d;

	Point receiveTarget;  // Point at which to place robot mouth
	Point passVector;     // Direction of pass
	if(_state == Setup) {
		receiveTarget = actionTarget;
		passVector = actionTarget - ball().pos;
	} else if(_state == Receive_PassKicking && partner) {
		OurRobot* passer = partner->robot;
		// The direction passer faces is the passing direction
		passVector = passer->kickerBar().center() - passer->pos;
		Line passLine(ball().pos, ball().pos + passVector);
		// Put mouth on the line
		// Using robot->pos would have caused the receiver to move backwards
		receiveTarget = passLine.nearestPoint(robot->kickerBar().center());
	} else if(_state == Receive_PassDone) {
		Line passLine(ball().pos, ball().pos + ball().vel);
		receiveTarget = passLine.nearestPoint(robot->kickerBar().center());
		passVector = ball().vel;
	} else if(_state == Done) {
		// Return start, if nowhere else
		receiveTarget = actionTarget;
		// passVector is (0,0)
	}

	// Calculate location of robot center
	receiveTarget += passVector **_backoffDistance;

	return receiveTarget;
}

float Gameplay::DumbReceive::receivePositionError()
{
	if ( !robot ) return 100;

	using namespace Geometry2d;
	float dist = (receivePosition() - robot->pos).mag();
	float distAngle = Line(receivePosition(), ball().pos).distTo(robot->pos);

	return std::max(dist, distAngle);
}

bool Gameplay::DumbReceive::isAtReceivePosition()
{
	return receivePositionError() < *_maxExecutionError;
}

bool Gameplay::DumbReceive::run()
{
	using namespace Geometry2d;

	if(!robot || !robot->visible) {
		return false;
	}

	// State changing
	if(_state == Setup) {
		if(isAtReceivePosition()) {
			if(partner && partner->isActing()) {
				_state = Receive_PassKicking;
			} else if(partner && partner->isDone()) {
				_state = Receive_PassDone;
			}
			// else if(!partner) {
			// 	_state = Receive_PassDone;
			// }
		}
	} else if(_state == Receive_PassKicking) {
		if(partner && partner->isSettingUp()) {
			_state = Setup;
		} else if(partner && partner->isDone()) {
			_state = Receive_PassDone;
		} else if(!partner) {
			_state = Receive_PassDone;
		}
	} else if(_state == Receive_PassDone) {
		if(_kickDelta == 0) {
			_kickDelta = timestamp();
		}

		if(robot->hasBall()) {
			_state = Done;
		} else if(timestamp() - _kickDelta > *_kickTimeout * 1000000) {  // Micro-seconds
			_state = Done;
		}
	} else if(_state == Done) {
		// Do nothing
	}

	// Debug visualization
	if(*_debug) {
		if(_state == Setup) {
			robot->addText(QString("DR: Setup"), Qt::yellow, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::yellow, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::yellow, QString("DumbReceive"));
		} else if(_state == Receive_PassKicking) {
			robot->addText(QString("DR: Receive PassKicking"), Qt::yellow, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::yellow, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::yellow, QString("DumbReceive"));
			// Draw line to show where we think the ball is going to travel
			state()->drawLine(ball().pos, receivePosition(), Qt::yellow, QString("DumbReceive"));
		} else if(_state == Receive_PassDone) {
			robot->addText(QString("DR: Receive PassDone"), Qt::green, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::green, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::green, QString("DumbReceive"));
			// Draw line to show where we think the ball is going to travel
			state()->drawLine(ball().pos, receivePosition(), Qt::green, QString("DumbReceive"));
		} else if(_state == Done) {
			// Do nothing
			robot->addText(QString("DR: Done"), Qt::red, QString("DumbReceive"));
		}
	}

	// Commands
	if(_state == Setup) {
		robot->avoidBall();
		robot->move(receivePosition(), false);
		robot->face(ball().pos);
	} else if(_state == Receive_PassKicking || _state == Receive_PassDone) {
		// FIXME: This has the side effect of always being on (even after play termination)
		// because it modifies an OurRobot internal variable.
		robot->disableAvoidBall();
		robot->move(receivePosition(), false);
		robot->face(ball().pos);
	}

	return true;
}
<|endoftext|>


<commit_before>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <af/dim4.hpp>
#include <Array.hpp>
#include <map>
#include <stdexcept>
#include <copy.hpp>
#include <JIT/Node.hpp>
#include <kernel_headers/jit.hpp>
#include <program.hpp>
#include <dispatch.hpp>
#include <err_opencl.hpp>
#include <functional>

namespace opencl
{

using JIT::Node;

using cl::Buffer;
using cl::Program;
using cl::Kernel;
using cl::make_kernel;
using cl::EnqueueArgs;
using cl::NDRange;
using std::string;
using std::stringstream;

static string getFuncName(Node *node, bool is_linear, bool *is_double)
{
    node->setId(0);

    stringstream hashName;
    stringstream funcName;

    if (is_linear) {
        funcName << "L_";
    } else {
        funcName << "G_";
    }

    std::string outName = node->getNameStr();
    funcName << outName;

    node->genKerName(funcName);

    string nameStr = funcName.str();
    funcName << nameStr;

    nameStr = nameStr + outName;
    string dblChars = "dDzZ";
    size_t loc = nameStr.find_first_of(dblChars);
    *is_double = (loc != std::string::npos);

    std::hash<std::string> hash_fn;
    hashName << "KER" << hash_fn(funcName.str());
    return hashName.str();
}

static string getKernelString(string funcName, Node *node, bool is_linear)
{
    stringstream kerStream;
    int id = node->getId();

    kerStream << "__kernel void" << "\n";

    kerStream << funcName;
    kerStream << "(" << "\n";

    node->genParams(kerStream);
    kerStream << "__global " << node->getTypeStr() << " *out, KParam oInfo," << "\n";
    kerStream << "uint groups_0, uint groups_1, uint num_odims)" << "\n";

    kerStream << "{" << "\n" << "\n";

    if (!is_linear) {

        kerStream << "uint id0 = 0, id1 = 0, id2 = 0, id3 = 0;\n";
        kerStream << "if (num_odims == 4) {\n";
        kerStream << "id2 = get_group_id(0) / groups_0;" << "\n";
        kerStream << "id3 = get_group_id(1) / groups_1;" << "\n";
        kerStream << "id0 = get_group_id(0) - id2 * groups_0;" << "\n";
        kerStream << "id1 = get_group_id(1) - id3 * groups_1;" << "\n";
        kerStream << "id0 = get_local_id(0) + id0 * get_local_size(0);" << "\n";
        kerStream << "id1 = get_local_id(1) + id1 * get_local_size(1);" << "\n";
        kerStream << " } else if (num_odims == 3) {\n";
        kerStream << "id2 = get_group_id(0) / groups_0;" << "\n";
        kerStream << "id3 = 0;" << "\n";
        kerStream << "id0 = get_group_id(0) - id2 * groups_0;" << "\n";
        kerStream << "id1 = get_global_id(1);" << "\n";
        kerStream << "id0 = get_local_id(0) + id0 * get_local_size(0);" << "\n";
        kerStream << " } else {\n";
        kerStream << "id3 = 0;" << "\n";
        kerStream << "id2 = 0;" << "\n";
        kerStream << "id1 = get_global_id(1);" << "\n";
        kerStream << "id0 = get_global_id(0);" << "\n";
        kerStream << "}\n";

        kerStream << "bool cond = " << "\n";
        kerStream << "id0 < oInfo.dims[0] && " << "\n";
        kerStream << "id1 < oInfo.dims[1] && " << "\n";
        kerStream << "id2 < oInfo.dims[2] && " << "\n";
        kerStream << "id3 < oInfo.dims[3];" << "\n" << "\n";

        kerStream << "if (!cond) return;" << "\n" << "\n";

        kerStream << "int idx = ";
        kerStream << "oInfo.strides[3] * id3 + oInfo.strides[2] * id2 + ";
        kerStream << "oInfo.strides[1] * id1 + id0 + oInfo.offset;" << "\n" << "\n";

    } else {

        kerStream << "uint groupId  = get_group_id(1) * get_num_groups(0) + get_group_id(0);" << "\n";
        kerStream << "uint threadId = get_local_id(0);" << "\n";
        kerStream << "int idx = groupId * get_local_size(0) * get_local_size(1) + threadId;" << "\n";
        kerStream << "if (idx >= oInfo.dims[3] * oInfo.strides[3]) return;" << "\n";
    }

    node->genOffsets(kerStream, is_linear);
    node->genFuncs(kerStream);
    kerStream << "\n";

    kerStream << "out[idx] = val"
           << id << ";"  << "\n";

    kerStream << "}" << "\n";

    return kerStream.str();
}

static Kernel getKernel(Node *node, bool is_linear)
{

    bool is_dbl = false;
    string funcName = getFuncName(node, is_linear, &is_dbl);

    typedef struct {
        Program* prog;
        Kernel* ker;
    } kc_entry_t;

    typedef std::map<string, kc_entry_t> kc_t;
    static kc_t kernelCaches[DeviceManager::MAX_DEVICES];
    int device = getActiveDeviceId();

    kc_t::iterator idx = kernelCaches[device].find(funcName);
    kc_entry_t entry;

    if (idx == kernelCaches[device].end()) {
        string jit_ker = getKernelString(funcName, node, is_linear);

        const char *ker_strs[] = {jit_cl, jit_ker.c_str()};
        const int ker_lens[] = {jit_cl_len, (int)jit_ker.size()};
        cl::Program prog;
        buildProgram(prog, 2, ker_strs, ker_lens, is_dbl ? string(" -D USE_DOUBLE") :  string(""));
        entry.prog = new cl::Program(prog);
        entry.ker = new Kernel(*entry.prog, funcName.c_str());

        kernelCaches[device][funcName] = entry;
    } else {
        entry = idx->second;
    }

    return *entry.ker;
}

void evalNodes(Param &out, Node *node)
{
    try {

        bool is_linear = node->isLinear(out.info.dims);
        Kernel ker = getKernel(node, is_linear);

        uint local_0 = 1;
        uint local_1 = 1;
        uint global_0 = 1;
        uint global_1 = 1;
        uint groups_0 = 1;
        uint groups_1 = 1;
        uint num_odims = 4;

        while (num_odims >= 1) {
            if (out.info.dims[num_odims - 1] == 1) num_odims--;
            else break;
        }

        if (is_linear) {
            local_0 = 256;
            uint out_elements = out.info.dims[3] * out.info.strides[3];
            uint groups = divup(out_elements, local_0);

            global_1 = divup(groups,     1000) * local_1;
            global_0 = divup(groups, global_1) * local_0;

        } else {
            local_0 = 64;
            local_1 =  4;

            groups_0 = divup(out.info.dims[0], local_0);
            groups_1 = divup(out.info.dims[1], local_1);

            global_0 = groups_0 * local_0 * out.info.dims[2];
            global_1 = groups_1 * local_1 * out.info.dims[3];
        }

        NDRange local(local_0, local_1);
        NDRange global(global_0, global_1);

        int args = node->setArgs(ker, 0);
        ker.setArg(args + 0, *out.data);
        ker.setArg(args + 1,  out.info);
        ker.setArg(args + 2,  groups_0);
        ker.setArg(args + 3,  groups_1);
        ker.setArg(args + 4,  num_odims);

        getQueue().enqueueNDRangeKernel(ker, cl::NullRange, global, local);

    } catch (const cl::Error &ex) {
        CL_TO_AF_ERROR(ex);
    }

}

}
<commit_msg>PERF: Speeding up JIT for 3D arrays in OpenCL backends<commit_after>/*******************************************************
 * Copyright (c) 2014, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#include <af/dim4.hpp>
#include <Array.hpp>
#include <map>
#include <stdexcept>
#include <copy.hpp>
#include <JIT/Node.hpp>
#include <kernel_headers/jit.hpp>
#include <program.hpp>
#include <dispatch.hpp>
#include <err_opencl.hpp>
#include <functional>

namespace opencl
{

using JIT::Node;

using cl::Buffer;
using cl::Program;
using cl::Kernel;
using cl::make_kernel;
using cl::EnqueueArgs;
using cl::NDRange;
using std::string;
using std::stringstream;

static string getFuncName(Node *node, bool is_linear, bool *is_double)
{
    node->setId(0);

    stringstream hashName;
    stringstream funcName;

    if (is_linear) {
        funcName << "L_";
    } else {
        funcName << "G_";
    }

    std::string outName = node->getNameStr();
    funcName << outName;

    node->genKerName(funcName);

    string nameStr = funcName.str();
    funcName << nameStr;

    nameStr = nameStr + outName;
    string dblChars = "dDzZ";
    size_t loc = nameStr.find_first_of(dblChars);
    *is_double = (loc != std::string::npos);

    std::hash<std::string> hash_fn;
    hashName << "KER" << hash_fn(funcName.str());
    return hashName.str();
}

static string getKernelString(string funcName, Node *node, bool is_linear)
{
    stringstream kerStream;
    int id = node->getId();

    kerStream << "__kernel void" << "\n";

    kerStream << funcName;
    kerStream << "(" << "\n";

    node->genParams(kerStream);
    kerStream << "__global " << node->getTypeStr() << " *out, KParam oInfo," << "\n";
    kerStream << "uint groups_0, uint groups_1, uint num_odims)" << "\n";

    kerStream << "{" << "\n" << "\n";

    if (!is_linear) {

        kerStream << "uint id0 = 0, id1 = 0, id2 = 0, id3 = 0;\n";
        kerStream << "if (num_odims > 2) {\n";

        kerStream << "id2 = get_group_id(0) / groups_0;" << "\n";
        kerStream << "id0 = get_group_id(0) - id2 * groups_0;" << "\n";
        kerStream << "id0 = get_local_id(0) + id0 * get_local_size(0);" << "\n";

        kerStream << "if (num_odims > 3) {\n";
        kerStream << "id3 = get_group_id(1) / groups_1;" << "\n";
        kerStream << "id1 = get_group_id(1) - id3 * groups_1;" << "\n";
        kerStream << "id1 = get_local_id(1) + id1 * get_local_size(1);" << "\n";
        kerStream << "} else {\n";
        kerStream << "id1 = get_global_id(1);" << "\n";
        kerStream << "}\n";
        kerStream << " } else {\n";
        kerStream << "id3 = 0;" << "\n";
        kerStream << "id2 = 0;" << "\n";
        kerStream << "id1 = get_global_id(1);" << "\n";
        kerStream << "id0 = get_global_id(0);" << "\n";
        kerStream << "}\n";

        kerStream << "bool cond = " << "\n";
        kerStream << "id0 < oInfo.dims[0] && " << "\n";
        kerStream << "id1 < oInfo.dims[1] && " << "\n";
        kerStream << "id2 < oInfo.dims[2] && " << "\n";
        kerStream << "id3 < oInfo.dims[3];" << "\n" << "\n";

        kerStream << "if (!cond) return;" << "\n" << "\n";

        kerStream << "int idx = ";
        kerStream << "oInfo.strides[3] * id3 + oInfo.strides[2] * id2 + ";
        kerStream << "oInfo.strides[1] * id1 + id0 + oInfo.offset;" << "\n" << "\n";

    } else {

        kerStream << "uint groupId  = get_group_id(1) * get_num_groups(0) + get_group_id(0);" << "\n";
        kerStream << "uint threadId = get_local_id(0);" << "\n";
        kerStream << "int idx = groupId * get_local_size(0) * get_local_size(1) + threadId;" << "\n";
        kerStream << "if (idx >= oInfo.dims[3] * oInfo.strides[3]) return;" << "\n";
    }

    node->genOffsets(kerStream, is_linear);
    node->genFuncs(kerStream);
    kerStream << "\n";

    kerStream << "out[idx] = val"
           << id << ";"  << "\n";

    kerStream << "}" << "\n";

    return kerStream.str();
}

static Kernel getKernel(Node *node, bool is_linear)
{

    bool is_dbl = false;
    string funcName = getFuncName(node, is_linear, &is_dbl);

    typedef struct {
        Program* prog;
        Kernel* ker;
    } kc_entry_t;

    typedef std::map<string, kc_entry_t> kc_t;
    static kc_t kernelCaches[DeviceManager::MAX_DEVICES];
    int device = getActiveDeviceId();

    kc_t::iterator idx = kernelCaches[device].find(funcName);
    kc_entry_t entry;

    if (idx == kernelCaches[device].end()) {
        string jit_ker = getKernelString(funcName, node, is_linear);

        const char *ker_strs[] = {jit_cl, jit_ker.c_str()};
        const int ker_lens[] = {jit_cl_len, (int)jit_ker.size()};
        cl::Program prog;
        buildProgram(prog, 2, ker_strs, ker_lens, is_dbl ? string(" -D USE_DOUBLE") :  string(""));
        entry.prog = new cl::Program(prog);
        entry.ker = new Kernel(*entry.prog, funcName.c_str());

        kernelCaches[device][funcName] = entry;
    } else {
        entry = idx->second;
    }

    return *entry.ker;
}

void evalNodes(Param &out, Node *node)
{
    try {

        bool is_linear = node->isLinear(out.info.dims);
        Kernel ker = getKernel(node, is_linear);

        uint local_0 = 1;
        uint local_1 = 1;
        uint global_0 = 1;
        uint global_1 = 1;
        uint groups_0 = 1;
        uint groups_1 = 1;
        uint num_odims = 4;

        while (num_odims >= 1) {
            if (out.info.dims[num_odims - 1] == 1) num_odims--;
            else break;
        }

        if (is_linear) {
            local_0 = 256;
            uint out_elements = out.info.dims[3] * out.info.strides[3];
            uint groups = divup(out_elements, local_0);

            global_1 = divup(groups,     1000) * local_1;
            global_0 = divup(groups, global_1) * local_0;

        } else {
            local_0 = 64;
            local_1 =  4;

            groups_0 = divup(out.info.dims[0], local_0);
            groups_1 = divup(out.info.dims[1], local_1);

            global_0 = groups_0 * local_0 * out.info.dims[2];
            global_1 = groups_1 * local_1 * out.info.dims[3];
        }

        NDRange local(local_0, local_1);
        NDRange global(global_0, global_1);

        int args = node->setArgs(ker, 0);
        ker.setArg(args + 0, *out.data);
        ker.setArg(args + 1,  out.info);
        ker.setArg(args + 2,  groups_0);
        ker.setArg(args + 3,  groups_1);
        ker.setArg(args + 4,  num_odims);

        getQueue().enqueueNDRangeKernel(ker, cl::NullRange, global, local);

    } catch (const cl::Error &ex) {
        CL_TO_AF_ERROR(ex);
    }

}

}
<|endoftext|>


<commit_before>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "lid_space_jobtest.h"

using BlockedReason = IBlockableMaintenanceJob::BlockedReason;

TEST_F(JobTest, handler_name_is_used_as_part_of_job_name)
{
    EXPECT_EQ("lid_space_compaction.myhandler", _job->getName());
}

TEST_F(JobTest, no_move_operation_is_created_if_lid_bloat_factor_is_below_limit)
{
    // 20% bloat < 30% allowed bloat
    addStats(10, {1,3,4,5,6,7,9}, {{9,2}});
    EXPECT_TRUE(run());
    assertNoWorkDone();
}

TEST_F(JobTest, no_move_operation_is_created_if_lid_bloat_is_below_limit)
{
    init(3, 0.1);
    // 20% bloat >= 10% allowed bloat BUT lid bloat (2) < allowed lid bloat (3)
    addStats(10, {1,3,4,5,6,7,9}, {{9,2}});
    EXPECT_TRUE(run());
    assertNoWorkDone();
}

TEST_F(JobTest, no_move_operation_is_created_and_compaction_is_initiated)
{
    // no documents to move: lowestFreeLid(7) > highestUsedLid(6)
    addStats(10, {1,2,3,4,5,6}, {{6,7}});

    // must scan to find that no documents should be moved
    endScan().compact();
    assertJobContext(0, 0, 0, 7, 1);
}

TEST_F(JobTest, one_move_operation_is_created_and_compaction_is_initiated)
{
    setupOneDocumentToCompact();
    EXPECT_FALSE(run()); // scan
    assertOneDocumentCompacted();
}

TEST_F(JobTest, job_returns_false_when_multiple_move_operations_or_compaction_are_needed)
{
    setupThreeDocumentsToCompact();
    EXPECT_FALSE(run());
    assertJobContext(2, 9, 1, 0, 0);
    EXPECT_FALSE(run());
    assertJobContext(3, 8, 2, 0, 0);
    EXPECT_FALSE(run());
    assertJobContext(4, 7, 3, 0, 0);
    endScan().compact();
    assertJobContext(4, 7, 3, 7, 1);
}

TEST_F(JobTest, job_can_restart_documents_scan_if_lid_bloat_is_still_to_large)
{
    init(ALLOWED_LID_BLOAT, ALLOWED_LID_BLOAT_FACTOR);
    addMultiStats(10, {{1,3,4,5,6,9},{1,2,4,5,6,8}},
                  {{9,2},   // 30% bloat: move 9 -> 2
                   {8,3},   // move 8 -> 3 (this should trigger rescan as the set of used docs have changed)
                   {6,7}}); // no documents to move

    EXPECT_FALSE(run());  // move 9 -> 2
    assertJobContext(2, 9, 1, 0, 0);
    EXPECT_EQ(1u, _handler->_iteratorCnt);
    // We simulate that the set of used docs have changed between these 2 runs
    EXPECT_FALSE(run()); // move 8 -> 3
    endScan();
    assertJobContext(3, 8, 2, 0, 0);
    EXPECT_EQ(2u, _handler->_iteratorCnt);
    endScan().compact();
    assertJobContext(3, 8, 2, 7, 1);
}

TEST_F(JobTest, held_lid_is_not_considered_free_and_blocks_job)
{
    // Lid 1 on hold or pendingHold, i.e. neither free nor used.
    addMultiStats(3, {{2}}, {{2, 3}});
    EXPECT_TRUE(run());
    assertNoWorkDone();
}

TEST_F(JobTest, held_lid_is_not_considered_free_with_only_compact)
{
    // Lid 1 on hold or pendingHold, i.e. neither free nor used.
    addMultiStats(10, {{2}}, {{2, 3}});
    EXPECT_FALSE(run());
    assertNoWorkDone();
    compact();
    assertJobContext(0, 0, 0, 3, 1);
}

TEST_F(JobTest, held_lids_are_not_considered_free_with_one_move)
{
    // Lids 1,2,3 on hold or pendingHold, i.e. neither free nor used.
    addMultiStats(10, {{5}}, {{5, 4}, {4, 5}});
    EXPECT_FALSE(run());
    assertJobContext(4, 5, 1, 0, 0);
    endScan().compact();
    assertJobContext(4, 5, 1, 5, 1);
}

TEST_F(JobTest, resource_starvation_blocks_lid_space_compaction)
{
    setupOneDocumentToCompact();
    _diskMemUsageNotifier.notify({{100, 0}, {100, 101}});
    EXPECT_TRUE(run()); // scan
    assertNoWorkDone();
}

TEST_F(JobTest, ending_resource_starvation_resumes_lid_space_compaction)
{
    setupOneDocumentToCompact();
    _diskMemUsageNotifier.notify({{100, 0}, {100, 101}});
    EXPECT_TRUE(run()); // scan
    assertNoWorkDone();
    _diskMemUsageNotifier.notify({{100, 0}, {100, 0}});
    assertOneDocumentCompacted();
}

TEST_F(JobTest, resource_limit_factor_adjusts_limit)
{
    init(ALLOWED_LID_BLOAT, ALLOWED_LID_BLOAT_FACTOR, 1.05);
    setupOneDocumentToCompact();
    _diskMemUsageNotifier.notify({{100, 0}, {100, 101}});
    EXPECT_FALSE(run()); // scan
    assertOneDocumentCompacted();
}

TEST_F(JobTest, delay_is_set_based_on_interval_and_is_max_300_secs)
{
    init_with_interval(301s);
    EXPECT_EQ(300s, _job->getDelay());
    EXPECT_EQ(301s, _job->getInterval());
}

TEST_F(JobTest, delay_is_set_based_on_interval_and_can_be_less_than_300_secs)
{
    init_with_interval(299s);
    EXPECT_EQ(299s, _job->getDelay());
    EXPECT_EQ(299s, _job->getInterval());
}

TEST_F(JobTest, job_is_disabled_when_node_is_retired)
{
    init_with_node_retired(true);
    setupOneDocumentToCompact();
    EXPECT_TRUE(run()); // not runnable, no work to do
    assertNoWorkDone();
}

TEST_F(JobTest, job_is_disabled_when_node_becomes_retired)
{
    init_with_node_retired(false);
    setupOneDocumentToCompact();
    notifyNodeRetired(true);
    EXPECT_TRUE(run()); // not runnable, no work to do
    assertNoWorkDone();
}

TEST_F(JobTest, job_is_re_enabled_when_node_is_no_longer_retired)
{
    init_with_node_retired(true);
    setupOneDocumentToCompact();
    EXPECT_TRUE(run()); // not runnable, no work to do
    assertNoWorkDone();
    notifyNodeRetired(false); // triggers running of job
    assertOneDocumentCompacted();
}

TEST_F(JobDisabledByRemoveOpsTest, config_is_propagated_to_remove_operations_rate_tracker)
{
    auto& remove_batch_tracker = _handler->_rm_listener->get_remove_batch_tracker();
    EXPECT_EQ(vespalib::from_s(21.0), remove_batch_tracker.get_time_budget_per_op());
    EXPECT_EQ(vespalib::from_s(21.0), remove_batch_tracker.get_time_budget_window());

    auto& remove_tracker = _handler->_rm_listener->get_remove_tracker();
    EXPECT_EQ(vespalib::from_s(20.0), remove_tracker.get_time_budget_per_op());
    EXPECT_EQ(vespalib::from_s(20.0), remove_tracker.get_time_budget_window());
}

TEST_F(JobDisabledByRemoveOpsTest, job_is_disabled_while_remove_batch_is_ongoing)
{
    job_is_disabled_while_remove_ops_are_ongoing(true);
}

TEST_F(JobDisabledByRemoveOpsTest, job_becomes_disabled_if_remove_batch_starts)
{
    job_becomes_disabled_if_remove_ops_starts(true);
}

TEST_F(JobDisabledByRemoveOpsTest, job_is_re_enabled_when_remove_batch_is_no_longer_ongoing)
{
    job_is_re_enabled_when_remove_ops_are_no_longer_ongoing(true);
}

TEST_F(JobDisabledByRemoveOpsTest, job_is_disabled_while_removes_are_ongoing)
{
    job_is_disabled_while_remove_ops_are_ongoing(false);
}

TEST_F(JobDisabledByRemoveOpsTest, job_becomes_disabled_if_removes_start)
{
    job_becomes_disabled_if_remove_ops_starts(false);
}

TEST_F(JobDisabledByRemoveOpsTest, job_is_re_enabled_when_removes_are_no_longer_ongoing)
{
    job_is_re_enabled_when_remove_ops_are_no_longer_ongoing(false);
}

TEST_F(MaxOutstandingJobTest, job_is_blocked_if_it_has_too_many_outstanding_move_operations_with_max_1)
{
    init(1);
    setupThreeDocumentsToCompact();

    assertRunToBlocked();
    assertJobContext(2, 9, 1, 0, 0);
    assertRunToBlocked();
    assertJobContext(2, 9, 1, 0, 0);

    unblockJob(1);
    assertRunToBlocked();
    assertJobContext(3, 8, 2, 0, 0);

    unblockJob(2);
    assertRunToBlocked();
    assertJobContext(4, 7, 3, 0, 0);

    unblockJob(3);
    endScan().compact();
    assertJobContext(4, 7, 3, 7, 1);
}

TEST_F(MaxOutstandingJobTest, job_is_blocked_if_it_has_too_many_outstanding_move_operations_with_max_2)
{
    init(2);
    setupThreeDocumentsToCompact();

    assertRunToNotBlocked();
    assertJobContext(2, 9, 1, 0, 0);
    assertRunToBlocked();
    assertJobContext(3, 8, 2, 0, 0);

    unblockJob(1);
    assertRunToNotBlocked();
    assertJobContext(4, 7, 3, 0, 0);
    endScan().compact();
    assertJobContext(4, 7, 3, 7, 1);
    sync();
}

GTEST_MAIN_RUN_ALL_TESTS()
<commit_msg>Unblock the last pending document.<commit_after>// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "lid_space_jobtest.h"

using BlockedReason = IBlockableMaintenanceJob::BlockedReason;

TEST_F(JobTest, handler_name_is_used_as_part_of_job_name)
{
    EXPECT_EQ("lid_space_compaction.myhandler", _job->getName());
}

TEST_F(JobTest, no_move_operation_is_created_if_lid_bloat_factor_is_below_limit)
{
    // 20% bloat < 30% allowed bloat
    addStats(10, {1,3,4,5,6,7,9}, {{9,2}});
    EXPECT_TRUE(run());
    assertNoWorkDone();
}

TEST_F(JobTest, no_move_operation_is_created_if_lid_bloat_is_below_limit)
{
    init(3, 0.1);
    // 20% bloat >= 10% allowed bloat BUT lid bloat (2) < allowed lid bloat (3)
    addStats(10, {1,3,4,5,6,7,9}, {{9,2}});
    EXPECT_TRUE(run());
    assertNoWorkDone();
}

TEST_F(JobTest, no_move_operation_is_created_and_compaction_is_initiated)
{
    // no documents to move: lowestFreeLid(7) > highestUsedLid(6)
    addStats(10, {1,2,3,4,5,6}, {{6,7}});

    // must scan to find that no documents should be moved
    endScan().compact();
    assertJobContext(0, 0, 0, 7, 1);
}

TEST_F(JobTest, one_move_operation_is_created_and_compaction_is_initiated)
{
    setupOneDocumentToCompact();
    EXPECT_FALSE(run()); // scan
    assertOneDocumentCompacted();
}

TEST_F(JobTest, job_returns_false_when_multiple_move_operations_or_compaction_are_needed)
{
    setupThreeDocumentsToCompact();
    EXPECT_FALSE(run());
    assertJobContext(2, 9, 1, 0, 0);
    EXPECT_FALSE(run());
    assertJobContext(3, 8, 2, 0, 0);
    EXPECT_FALSE(run());
    assertJobContext(4, 7, 3, 0, 0);
    endScan().compact();
    assertJobContext(4, 7, 3, 7, 1);
}

TEST_F(JobTest, job_can_restart_documents_scan_if_lid_bloat_is_still_to_large)
{
    init(ALLOWED_LID_BLOAT, ALLOWED_LID_BLOAT_FACTOR);
    addMultiStats(10, {{1,3,4,5,6,9},{1,2,4,5,6,8}},
                  {{9,2},   // 30% bloat: move 9 -> 2
                   {8,3},   // move 8 -> 3 (this should trigger rescan as the set of used docs have changed)
                   {6,7}}); // no documents to move

    EXPECT_FALSE(run());  // move 9 -> 2
    assertJobContext(2, 9, 1, 0, 0);
    EXPECT_EQ(1u, _handler->_iteratorCnt);
    // We simulate that the set of used docs have changed between these 2 runs
    EXPECT_FALSE(run()); // move 8 -> 3
    endScan();
    assertJobContext(3, 8, 2, 0, 0);
    EXPECT_EQ(2u, _handler->_iteratorCnt);
    endScan().compact();
    assertJobContext(3, 8, 2, 7, 1);
}

TEST_F(JobTest, held_lid_is_not_considered_free_and_blocks_job)
{
    // Lid 1 on hold or pendingHold, i.e. neither free nor used.
    addMultiStats(3, {{2}}, {{2, 3}});
    EXPECT_TRUE(run());
    assertNoWorkDone();
}

TEST_F(JobTest, held_lid_is_not_considered_free_with_only_compact)
{
    // Lid 1 on hold or pendingHold, i.e. neither free nor used.
    addMultiStats(10, {{2}}, {{2, 3}});
    EXPECT_FALSE(run());
    assertNoWorkDone();
    compact();
    assertJobContext(0, 0, 0, 3, 1);
}

TEST_F(JobTest, held_lids_are_not_considered_free_with_one_move)
{
    // Lids 1,2,3 on hold or pendingHold, i.e. neither free nor used.
    addMultiStats(10, {{5}}, {{5, 4}, {4, 5}});
    EXPECT_FALSE(run());
    assertJobContext(4, 5, 1, 0, 0);
    endScan().compact();
    assertJobContext(4, 5, 1, 5, 1);
}

TEST_F(JobTest, resource_starvation_blocks_lid_space_compaction)
{
    setupOneDocumentToCompact();
    _diskMemUsageNotifier.notify({{100, 0}, {100, 101}});
    EXPECT_TRUE(run()); // scan
    assertNoWorkDone();
}

TEST_F(JobTest, ending_resource_starvation_resumes_lid_space_compaction)
{
    setupOneDocumentToCompact();
    _diskMemUsageNotifier.notify({{100, 0}, {100, 101}});
    EXPECT_TRUE(run()); // scan
    assertNoWorkDone();
    _diskMemUsageNotifier.notify({{100, 0}, {100, 0}});
    assertOneDocumentCompacted();
}

TEST_F(JobTest, resource_limit_factor_adjusts_limit)
{
    init(ALLOWED_LID_BLOAT, ALLOWED_LID_BLOAT_FACTOR, 1.05);
    setupOneDocumentToCompact();
    _diskMemUsageNotifier.notify({{100, 0}, {100, 101}});
    EXPECT_FALSE(run()); // scan
    assertOneDocumentCompacted();
}

TEST_F(JobTest, delay_is_set_based_on_interval_and_is_max_300_secs)
{
    init_with_interval(301s);
    EXPECT_EQ(300s, _job->getDelay());
    EXPECT_EQ(301s, _job->getInterval());
}

TEST_F(JobTest, delay_is_set_based_on_interval_and_can_be_less_than_300_secs)
{
    init_with_interval(299s);
    EXPECT_EQ(299s, _job->getDelay());
    EXPECT_EQ(299s, _job->getInterval());
}

TEST_F(JobTest, job_is_disabled_when_node_is_retired)
{
    init_with_node_retired(true);
    setupOneDocumentToCompact();
    EXPECT_TRUE(run()); // not runnable, no work to do
    assertNoWorkDone();
}

TEST_F(JobTest, job_is_disabled_when_node_becomes_retired)
{
    init_with_node_retired(false);
    setupOneDocumentToCompact();
    notifyNodeRetired(true);
    EXPECT_TRUE(run()); // not runnable, no work to do
    assertNoWorkDone();
}

TEST_F(JobTest, job_is_re_enabled_when_node_is_no_longer_retired)
{
    init_with_node_retired(true);
    setupOneDocumentToCompact();
    EXPECT_TRUE(run()); // not runnable, no work to do
    assertNoWorkDone();
    notifyNodeRetired(false); // triggers running of job
    assertOneDocumentCompacted();
}

TEST_F(JobDisabledByRemoveOpsTest, config_is_propagated_to_remove_operations_rate_tracker)
{
    auto& remove_batch_tracker = _handler->_rm_listener->get_remove_batch_tracker();
    EXPECT_EQ(vespalib::from_s(21.0), remove_batch_tracker.get_time_budget_per_op());
    EXPECT_EQ(vespalib::from_s(21.0), remove_batch_tracker.get_time_budget_window());

    auto& remove_tracker = _handler->_rm_listener->get_remove_tracker();
    EXPECT_EQ(vespalib::from_s(20.0), remove_tracker.get_time_budget_per_op());
    EXPECT_EQ(vespalib::from_s(20.0), remove_tracker.get_time_budget_window());
}

TEST_F(JobDisabledByRemoveOpsTest, job_is_disabled_while_remove_batch_is_ongoing)
{
    job_is_disabled_while_remove_ops_are_ongoing(true);
}

TEST_F(JobDisabledByRemoveOpsTest, job_becomes_disabled_if_remove_batch_starts)
{
    job_becomes_disabled_if_remove_ops_starts(true);
}

TEST_F(JobDisabledByRemoveOpsTest, job_is_re_enabled_when_remove_batch_is_no_longer_ongoing)
{
    job_is_re_enabled_when_remove_ops_are_no_longer_ongoing(true);
}

TEST_F(JobDisabledByRemoveOpsTest, job_is_disabled_while_removes_are_ongoing)
{
    job_is_disabled_while_remove_ops_are_ongoing(false);
}

TEST_F(JobDisabledByRemoveOpsTest, job_becomes_disabled_if_removes_start)
{
    job_becomes_disabled_if_remove_ops_starts(false);
}

TEST_F(JobDisabledByRemoveOpsTest, job_is_re_enabled_when_removes_are_no_longer_ongoing)
{
    job_is_re_enabled_when_remove_ops_are_no_longer_ongoing(false);
}

TEST_F(MaxOutstandingJobTest, job_is_blocked_if_it_has_too_many_outstanding_move_operations_with_max_1)
{
    init(1);
    setupThreeDocumentsToCompact();

    assertRunToBlocked();
    assertJobContext(2, 9, 1, 0, 0);
    assertRunToBlocked();
    assertJobContext(2, 9, 1, 0, 0);

    unblockJob(1);
    assertRunToBlocked();
    assertJobContext(3, 8, 2, 0, 0);

    unblockJob(2);
    assertRunToBlocked();
    assertJobContext(4, 7, 3, 0, 0);

    unblockJob(3);
    endScan().compact();
    assertJobContext(4, 7, 3, 7, 1);
}

TEST_F(MaxOutstandingJobTest, job_is_blocked_if_it_has_too_many_outstanding_move_operations_with_max_2)
{
    init(2);
    setupThreeDocumentsToCompact();

    assertRunToNotBlocked();
    assertJobContext(2, 9, 1, 0, 0);
    assertRunToBlocked();
    assertJobContext(3, 8, 2, 0, 0);

    unblockJob(1);
    assertRunToNotBlocked();
    assertJobContext(4, 7, 3, 0, 0);
    unblockJob(1);
    endScan().compact();
    assertJobContext(4, 7, 3, 7, 1);
    sync();
}

GTEST_MAIN_RUN_ALL_TESTS()
<|endoftext|>


<commit_before>#include <LightEffectDriver.h>

LightEffectDriver::LightEffectDriver(byte* pins, byte pinNumber) {
  _pinNumber = pinNumber;
  _pins = new byte[pinNumber];
  _minValues = new byte[pinNumber];
  _maxValues = new byte[pinNumber];
  _pinValues = new byte[pinNumber];
  for (int i=0; i < pinNumber; ++i) {
    _pins[i] = pins[i];
    _minValues[i] = 0;
    _maxValues[i] = 255;
  }
}

/* TODO: Init state variables */
/* TODO: Deconstruct _pins, _minValues, _maxValues, _pinValues, ... */

void LightEffectDriver::update() {
  /* Check the last change: */
  if (millis() - _lastChange < _changeInterval) {
    return;
  };
  /* Check the effect: */
  if (_effect == SWITCH) {
    /* Switching */
    _indexPin1 = _chooseNextPinIndex(_indexPin1);
    for (int i=0; i < _pinNumber; ++i) {
      if (i == _indexPin1) {
        _pinValues[i] = _maxValues[i];
      } else {
        _pinValues[i] = _minValues[i];
      };
    }
    _changeDirection();
  } else if (_effect == FADE) {
    /* Fading */
    /* TODO */
  } else if (_effect == FADEOVER) {
    /* Fading over */
    /* TODO */
  };
  /* Set pins: */
  _setPins();
  /* Set last change: */
  _lastChange = millis();
}

void LightEffectDriver::setEffect(LightEffect effect, LightEffectOrder order, LightEffectDirection direction, LightEffectCurve curve) {
  /* TODO: Check arguments */
  _effect = effect;
  _order = order;
  _direction = direction;
  _curve = curve;
  /* Update: */
  _calculateChangeInterval();
}

void LightEffectDriver::setEffectSpeed(byte speed) {
  _speed = speed;
  /* Update: */
  _calculateChangeInterval();
}

void LightEffectDriver::setMinValues(byte* minValues) {
  for (int i=0; i < _pinNumber; ++i) {
    _minValues[i] = minValues[i];
  }
}

void LightEffectDriver::setMaxValues(byte* maxValues) {
  for (int i=0; i < _pinNumber; ++i) {
    _maxValues[i] = maxValues[i];
  }
}

void LightEffectDriver::_calculateChangeInterval() {
  // TODO: _changeInterval = someAlgorithm(_speed, _effect);
}

byte LightEffectDriver::_chooseNextPinIndex(byte currentIndex) {
  if (_order == RANDOM) {
    /* Random order, choose a random pin to be the next: */
    return random(0, _pinNumber);
  } else {
    /* Check for direction: */
    if (_getEffectDirection() == UP) {
      /* Counting up, check if we are at the last pin: */
      if (currentIndex == _pinNumber-1) {
        return 0;
      } else {
        return currentIndex+1;
      };
    } else {
      /* Counting down, check if we are at the first pin: */
      if (currentIndex == 0) {
        return _pinNumber;
      } else {
        return currentIndex-1;
      };
    };
  };
}

void LightEffectDriver::_setPins() {
  for (int i=0; i < _pinNumber; ++i) {
    switch (_curve) {
      case INV:
        /* inverted */
        analogWrite(_pins[i], 255-_pinValues[i]);
        break;
      case EXP:
        /* exponential */
        analogWrite(_pins[i], pgm_read_byte(&_expTable[_pinValues[i]]));
        break;
      case EXPINV:
        /* exponential & inverted */
        analogWrite(_pins[i], pgm_read_byte(&_expTable[255-_pinValues[i]]));
        break;
      default:
        /* linear */
        analogWrite(_pins[i], _pinValues[i]);
    }
  }
}

LightEffectDirection LightEffectDriver::_getEffectDirection() {
  if (_direction == UPDOWN) {
    return _directionState;
  } else {
    return _direction;
  };
}

/* not for FADEOVER */
void LightEffectDriver::_changeDirection() {
  if (_order == RANDOM || _direction != UPDOWN) {
    return;
  };
  if (_direction == UP && _indexPin1 == _pinNumber-1) {
    _direction = DOWN;
  } else if (_direction == DOWN && _indexPin1 == 0) {
    _direction = UP;
  };
}

/*
 * f(x) = a * e^(x/b) with a = f(0) and b = 255/ln(255/a)
 *
 * Generated with python:
 * import math
 * for i in range(256):
 *   print(int(0.99*math.exp(i/255*math.log(255/0.99))), end=", ")
*/
const uint8_t LightEffectDriver::_expTable[256] PROGMEM = {
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4,
  4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8,
  8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15,
  15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 19, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25,
  26, 27, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44,
  45, 46, 47, 48, 49, 50, 52, 53, 54, 55, 56, 58, 59, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, 75, 77,
  78, 80, 82, 84, 85, 87, 89, 91, 93, 95, 97, 99, 102, 104, 106, 109, 111, 113, 116, 119, 121, 124,
  127, 129, 132, 135, 138, 141, 144, 147, 151, 154, 157, 161, 164, 168, 172, 176, 179, 183, 188, 192,
  196, 200, 205, 209, 214, 218, 223, 228, 233, 238, 244, 249, 255
};
<commit_msg>variable initialization<commit_after>#include <LightEffectDriver.h>

LightEffectDriver::LightEffectDriver(byte* pins, byte pinNumber) {
  /* TODO: Check pinNumber */
  /* Set scalar variables: */
  _pinNumber = pinNumber;
  _speed = 100;
  _effect = SWITCH;
  _order = ORDERED;
  _direction = UP;
  _curve = EXP;
  _lastChange = 0;
  _indexPin1 = 0; /* TODO: Maybe init indexPin2? */
  _directionState = UP;
  /* Calculate changeInterval: */
  _calculateChangeInterval();
  /* Set array variables: */
  _pins = new byte[pinNumber];
  _minValues = new byte[pinNumber];
  _maxValues = new byte[pinNumber];
  _pinValues = new byte[pinNumber];
  for (int i=0; i < pinNumber; ++i) {
    _pins[i] = pins[i];
    _minValues[i] = 0;
    _maxValues[i] = 255;
    _pinValues[i] = 0;
  }
}

/* TODO: Init state variables */
/* TODO: Deconstruct _pins, _minValues, _maxValues, _pinValues, ... */

void LightEffectDriver::update() {
  /* Check the last change: */
  if (millis() - _lastChange < _changeInterval) {
    return;
  };
  /* Check the effect: */
  if (_effect == SWITCH) {
    /* Switching */
    _indexPin1 = _chooseNextPinIndex(_indexPin1);
    for (int i=0; i < _pinNumber; ++i) {
      if (i == _indexPin1) {
        _pinValues[i] = _maxValues[i];
      } else {
        _pinValues[i] = _minValues[i];
      };
    }
    _changeDirection();
  } else if (_effect == FADE) {
    /* Fading */
    /* TODO */
  } else if (_effect == FADEOVER) {
    /* Fading over */
    /* TODO */
  };
  /* Set pins: */
  _setPins();
  /* Set last change: */
  _lastChange = millis();
}

void LightEffectDriver::setEffect(LightEffect effect, LightEffectOrder order, LightEffectDirection direction, LightEffectCurve curve) {
  /* TODO: Check arguments */
  _effect = effect;
  _order = order;
  _direction = direction;
  _curve = curve;
  /* Update: */
  _calculateChangeInterval();
}

void LightEffectDriver::setEffectSpeed(byte speed) {
  _speed = speed;
  /* Update: */
  _calculateChangeInterval();
}

void LightEffectDriver::setMinValues(byte* minValues) {
  for (int i=0; i < _pinNumber; ++i) {
    _minValues[i] = minValues[i];
  }
}

void LightEffectDriver::setMaxValues(byte* maxValues) {
  for (int i=0; i < _pinNumber; ++i) {
    _maxValues[i] = maxValues[i];
  }
}

void LightEffectDriver::_calculateChangeInterval() {
  // TODO: _changeInterval = someAlgorithm(_speed, _effect);
}

byte LightEffectDriver::_chooseNextPinIndex(byte currentIndex) {
  if (_order == RANDOM) {
    /* Random order, choose a random pin to be the next: */
    return random(0, _pinNumber);
  } else {
    /* Check for direction: */
    if (_getEffectDirection() == UP) {
      /* Counting up, check if we are at the last pin: */
      if (currentIndex == _pinNumber-1) {
        return 0;
      } else {
        return currentIndex+1;
      };
    } else {
      /* Counting down, check if we are at the first pin: */
      if (currentIndex == 0) {
        return _pinNumber;
      } else {
        return currentIndex-1;
      };
    };
  };
}

void LightEffectDriver::_setPins() {
  for (int i=0; i < _pinNumber; ++i) {
    switch (_curve) {
      case INV:
        /* inverted */
        analogWrite(_pins[i], 255-_pinValues[i]);
        break;
      case EXP:
        /* exponential */
        analogWrite(_pins[i], pgm_read_byte(&_expTable[_pinValues[i]]));
        break;
      case EXPINV:
        /* exponential & inverted */
        analogWrite(_pins[i], pgm_read_byte(&_expTable[255-_pinValues[i]]));
        break;
      default:
        /* linear */
        analogWrite(_pins[i], _pinValues[i]);
    }
  }
}

LightEffectDirection LightEffectDriver::_getEffectDirection() {
  if (_direction == UPDOWN) {
    return _directionState;
  } else {
    return _direction;
  };
}

/* not for FADEOVER */
void LightEffectDriver::_changeDirection() {
  if (_order == RANDOM || _direction != UPDOWN) {
    return;
  };
  if (_direction == UP && _indexPin1 == _pinNumber-1) {
    _direction = DOWN;
  } else if (_direction == DOWN && _indexPin1 == 0) {
    _direction = UP;
  };
}

/*
 * f(x) = a * e^(x/b) with a = f(0) and b = 255/ln(255/a)
 *
 * Generated with python:
 * import math
 * for i in range(256):
 *   print(int(0.99*math.exp(i/255*math.log(255/0.99))), end=", ")
*/
const uint8_t LightEffectDriver::_expTable[256] PROGMEM = {
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4,
  4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8,
  8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15,
  15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 19, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25,
  26, 27, 27, 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 40, 41, 42, 43, 44,
  45, 46, 47, 48, 49, 50, 52, 53, 54, 55, 56, 58, 59, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, 75, 77,
  78, 80, 82, 84, 85, 87, 89, 91, 93, 95, 97, 99, 102, 104, 106, 109, 111, 113, 116, 119, 121, 124,
  127, 129, 132, 135, 138, 141, 144, 147, 151, 154, 157, 161, 164, 168, 172, 176, 179, 183, 188, 192,
  196, 200, 205, 209, 214, 218, 223, 228, 233, 238, 244, 249, 255
};
<|endoftext|>


<commit_before>#include "DumbReceive.hpp"
#include "Utils.hpp"
#include <math.h>

namespace Gameplay { REGISTER_CONFIGURABLE(DumbReceive) }

ConfigDouble *Gameplay::DumbReceive::_backoffDistance;
ConfigDouble *Gameplay::DumbReceive::_maxExecutionError;
ConfigDouble *Gameplay::DumbReceive::_kickTimeout;

ConfigBool *Gameplay::DumbReceive::_debug;

void Gameplay::DumbReceive::createConfiguration(Configuration *cfg)
{
	_backoffDistance = new ConfigDouble(cfg, "DumbReceive/Backoff Distance", 0.078674155);
	_maxExecutionError = new ConfigDouble(cfg, "DumbReceive/Max Execution Error", 0.015);
	_kickTimeout = new ConfigDouble(cfg, "DumbReceive/Kick Timeout", 3.0);

	_debug = new ConfigBool(cfg, "StablePivotKick/Debug", true);
}

Gameplay::DumbReceive::DumbReceive(GameplayModule *gameplay)
	: ActionBehavior(gameplay),
	  _state(Setup),
	  _success(false),
	  _kickDelta(0)
{
}

bool Gameplay::DumbReceive::isSettingUp()
{
	return _state == Setup;
}

bool Gameplay::DumbReceive::isSetup()
{
	return isAtReceivePosition() && _state == Setup;
}

bool Gameplay::DumbReceive::isActing()
{
	return _state == Receive_PassKicking || _state == Receive_PassDone;
}

bool Gameplay::DumbReceive::isDone()
{
	return _state == Done;
}

void Gameplay::DumbReceive::restart()
{
	_state = Setup;
	_success = false;
	_kickDelta = 0;
}

Geometry2d::Point Gameplay::DumbReceive::receivePosition()
{
	using namespace Geometry2d;

	Point receiveTarget;  // Point at which to place robot mouth
	Point passVector;     // Direction of pass
	if(_state == Setup) {
		receiveTarget = actionTarget;
		passVector = actionTarget - ball().pos;
	} else if(_state == Receive_PassKicking && partner) {
		OurRobot* passer = partner->robot;
		// The direction passer faces is the passing direction
		passVector = passer->kickerBar().center() - passer->pos;
		Line passLine(ball().pos, ball().pos + passVector);
		// Put mouth on the line
		// Using robot->pos would have caused the receiver to move backwards
		receiveTarget = passLine.nearestPoint(robot->kickerBar().center());
	} else if(_state == Receive_PassDone) {
		Line passLine(ball().pos, ball().pos + ball().vel);
		receiveTarget = passLine.nearestPoint(robot->kickerBar().center());
		passVector = ball().vel;
	} else if(_state == Done) {
		// Return start, if nowhere else
		receiveTarget = actionTarget;
		// passVector is (0,0)
	}

	// Calculate location of robot center
	receiveTarget += passVector **_backoffDistance;

	return receiveTarget;
}

float Gameplay::DumbReceive::receivePositionError()
{
	using namespace Geometry2d;
	float dist = (receivePosition() - robot->pos).mag();
	float distAngle = Line(receivePosition(), ball().pos).distTo(robot->pos);

	return std::max(dist, distAngle);
}

bool Gameplay::DumbReceive::isAtReceivePosition()
{
	return receivePositionError() < *_maxExecutionError;
}

bool Gameplay::DumbReceive::run()
{
	using namespace Geometry2d;

	if(!robot || !robot->visible) {
		return false;
	}

	// State changing
	if(_state == Setup) {
		if(isAtReceivePosition()) {
			if(partner && partner->isActing()) {
				_state = Receive_PassKicking;
			} else if(partner && partner->isDone()) {
				_state = Receive_PassDone;
			} else if(!partner) {
				_state = Receive_PassDone;
			}
		}
	} else if(_state == Receive_PassKicking) {
		if(partner && partner->isSettingUp()) {
			_state = Setup;
		} else if(partner && partner->isDone()) {
			_state = Receive_PassDone;
		} else if(!partner) {
			_state = Receive_PassDone;
		}
	} else if(_state == Receive_PassDone) {
		if(_kickDelta == 0) {
			_kickDelta = timestamp();
		}

		if(robot->hasBall()) {
			_state = Done;
		} else if(timestamp() - _kickDelta > *_kickTimeout * 1000000) {  // Micro-seconds
			_state = Done;
		}
	} else if(_state == Done) {
		// Do nothing
	}

	// Debug visualization
	if(*_debug) {
		if(_state == Setup) {
			robot->addText(QString("DR: Setup"), Qt::yellow, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::yellow, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::yellow, QString("DumbReceive"));
		} else if(_state == Receive_PassKicking) {
			robot->addText(QString("DR: Receive PassKicking"), Qt::yellow, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::yellow, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::yellow, QString("DumbReceive"));
			// Draw line to show where we think the ball is going to travel
			state()->drawLine(ball().pos, receivePosition(), Qt::yellow, QString("DumbReceive"));
		} else if(_state == Receive_PassDone) {
			robot->addText(QString("DR: Receive PassDone"), Qt::green, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::green, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::green, QString("DumbReceive"));
			// Draw line to show where we think the ball is going to travel
			state()->drawLine(ball().pos, receivePosition(), Qt::green, QString("DumbReceive"));
		} else if(_state == Done) {
			// Do nothing
			robot->addText(QString("DR: Done"), Qt::red, QString("DumbReceive"));
		}
	}

	// Commands
	if(_state == Setup) {
		robot->avoidBall();
		robot->move(receivePosition(), false);
		robot->face(ball().pos);
	} else if(_state == Receive_PassKicking || _state == Receive_PassDone) {
		// FIXME: This has the side effect of always being on (even after play termination)
		// because it modifies an OurRobot internal variable.
		robot->disableAvoidBall();
		robot->move(receivePosition(), false);
		robot->face(ball().pos);
	}

	return true;
}
<commit_msg>DumbReceive doesnt exit now when it doesnt have a partner<commit_after>#include "DumbReceive.hpp"
#include "Utils.hpp"
#include <math.h>

namespace Gameplay { REGISTER_CONFIGURABLE(DumbReceive) }

ConfigDouble *Gameplay::DumbReceive::_backoffDistance;
ConfigDouble *Gameplay::DumbReceive::_maxExecutionError;
ConfigDouble *Gameplay::DumbReceive::_kickTimeout;

ConfigBool *Gameplay::DumbReceive::_debug;

void Gameplay::DumbReceive::createConfiguration(Configuration *cfg)
{
	_backoffDistance = new ConfigDouble(cfg, "DumbReceive/Backoff Distance", 0.078674155);
	_maxExecutionError = new ConfigDouble(cfg, "DumbReceive/Max Execution Error", 0.015);
	_kickTimeout = new ConfigDouble(cfg, "DumbReceive/Kick Timeout", 3.0);

	_debug = new ConfigBool(cfg, "StablePivotKick/Debug", true);
}

Gameplay::DumbReceive::DumbReceive(GameplayModule *gameplay)
	: ActionBehavior(gameplay),
	  _state(Setup),
	  _success(false),
	  _kickDelta(0)
{
}

bool Gameplay::DumbReceive::isSettingUp()
{
	return _state == Setup;
}

bool Gameplay::DumbReceive::isSetup()
{
	return isAtReceivePosition() && _state == Setup;
}

bool Gameplay::DumbReceive::isActing()
{
	return _state == Receive_PassKicking || _state == Receive_PassDone;
}

bool Gameplay::DumbReceive::isDone()
{
	return _state == Done;
}

void Gameplay::DumbReceive::restart()
{
	_state = Setup;
	_success = false;
	_kickDelta = 0;
}

Geometry2d::Point Gameplay::DumbReceive::receivePosition()
{
	using namespace Geometry2d;

	Point receiveTarget;  // Point at which to place robot mouth
	Point passVector;     // Direction of pass
	if(_state == Setup) {
		receiveTarget = actionTarget;
		passVector = actionTarget - ball().pos;
	} else if(_state == Receive_PassKicking && partner) {
		OurRobot* passer = partner->robot;
		// The direction passer faces is the passing direction
		passVector = passer->kickerBar().center() - passer->pos;
		Line passLine(ball().pos, ball().pos + passVector);
		// Put mouth on the line
		// Using robot->pos would have caused the receiver to move backwards
		receiveTarget = passLine.nearestPoint(robot->kickerBar().center());
	} else if(_state == Receive_PassDone) {
		Line passLine(ball().pos, ball().pos + ball().vel);
		receiveTarget = passLine.nearestPoint(robot->kickerBar().center());
		passVector = ball().vel;
	} else if(_state == Done) {
		// Return start, if nowhere else
		receiveTarget = actionTarget;
		// passVector is (0,0)
	}

	// Calculate location of robot center
	receiveTarget += passVector **_backoffDistance;

	return receiveTarget;
}

float Gameplay::DumbReceive::receivePositionError()
{
	using namespace Geometry2d;
	float dist = (receivePosition() - robot->pos).mag();
	float distAngle = Line(receivePosition(), ball().pos).distTo(robot->pos);

	return std::max(dist, distAngle);
}

bool Gameplay::DumbReceive::isAtReceivePosition()
{
	return receivePositionError() < *_maxExecutionError;
}

bool Gameplay::DumbReceive::run()
{
	using namespace Geometry2d;

	if(!robot || !robot->visible) {
		return false;
	}

	// State changing
	if(_state == Setup) {
		if(isAtReceivePosition()) {
			if(partner && partner->isActing()) {
				_state = Receive_PassKicking;
			} else if(partner && partner->isDone()) {
				_state = Receive_PassDone;
			}
			// else if(!partner) {
			// 	_state = Receive_PassDone;
			// }
		}
	} else if(_state == Receive_PassKicking) {
		if(partner && partner->isSettingUp()) {
			_state = Setup;
		} else if(partner && partner->isDone()) {
			_state = Receive_PassDone;
		} else if(!partner) {
			_state = Receive_PassDone;
		}
	} else if(_state == Receive_PassDone) {
		if(_kickDelta == 0) {
			_kickDelta = timestamp();
		}

		if(robot->hasBall()) {
			_state = Done;
		} else if(timestamp() - _kickDelta > *_kickTimeout * 1000000) {  // Micro-seconds
			_state = Done;
		}
	} else if(_state == Done) {
		// Do nothing
	}

	// Debug visualization
	if(*_debug) {
		if(_state == Setup) {
			robot->addText(QString("DR: Setup"), Qt::yellow, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::yellow, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::yellow, QString("DumbReceive"));
		} else if(_state == Receive_PassKicking) {
			robot->addText(QString("DR: Receive PassKicking"), Qt::yellow, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::yellow, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::yellow, QString("DumbReceive"));
			// Draw line to show where we think the ball is going to travel
			state()->drawLine(ball().pos, receivePosition(), Qt::yellow, QString("DumbReceive"));
		} else if(_state == Receive_PassDone) {
			robot->addText(QString("DR: Receive PassDone"), Qt::green, QString("DumbReceive"));
			robot->addText(QString("DR error: %1").arg(receivePositionError()), Qt::green, QString("DumbReceive"));
			// Draw circle at pass position
			state()->drawCircle(receivePosition(), Robot_Radius + 0.05, Qt::green, QString("DumbReceive"));
			// Draw line to show where we think the ball is going to travel
			state()->drawLine(ball().pos, receivePosition(), Qt::green, QString("DumbReceive"));
		} else if(_state == Done) {
			// Do nothing
			robot->addText(QString("DR: Done"), Qt::red, QString("DumbReceive"));
		}
	}

	// Commands
	if(_state == Setup) {
		robot->avoidBall();
		robot->move(receivePosition(), false);
		robot->face(ball().pos);
	} else if(_state == Receive_PassKicking || _state == Receive_PassDone) {
		// FIXME: This has the side effect of always being on (even after play termination)
		// because it modifies an OurRobot internal variable.
		robot->disableAvoidBall();
		robot->move(receivePosition(), false);
		robot->face(ball().pos);
	}

	return true;
}
<|endoftext|>