<commit_before>// 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 <algorithm>
#include <list>
#include <map>

#include "chrome/browser/browser_thread.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/login/login_prompt.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "net/base/auth.h"

namespace {

class LoginPromptBrowserTest : public InProcessBrowserTest {
 public:
  LoginPromptBrowserTest()
      : bad_password_(L"incorrect"), bad_username_(L"nouser") {
    set_show_window(true);

    auth_map_[L"foo"] = AuthInfo(L"testuser", L"foopassword");
    auth_map_[L"bar"] = AuthInfo(L"testuser", L"barpassword");
  }

 protected:
  void SetAuthFor(LoginHandler* handler);

  struct AuthInfo {
    std::wstring username_;
    std::wstring password_;

    AuthInfo() {}

    AuthInfo(const std::wstring username,
             const std::wstring password)
        : username_(username), password_(password) {}
  };

  std::map<std::wstring, AuthInfo> auth_map_;
  std::wstring bad_password_;
  std::wstring bad_username_;
};

void LoginPromptBrowserTest::SetAuthFor(LoginHandler* handler) {
  const net::AuthChallengeInfo* challenge = handler->auth_info();

  ASSERT_TRUE(challenge);
  std::map<std::wstring, AuthInfo>::iterator i =
      auth_map_.find(challenge->realm);
  EXPECT_TRUE(auth_map_.end() != i);
  if (i != auth_map_.end()) {
    const AuthInfo& info = i->second;
    handler->SetAuth(info.username_, info.password_);
  }
}

// Maintains a set of LoginHandlers that are currently active and
// keeps a count of the notifications that were observed.
class LoginPromptBrowserTestObserver : public NotificationObserver {
 public:
  LoginPromptBrowserTestObserver()
      : auth_needed_count_(0),
        auth_supplied_count_(0),
        auth_cancelled_count_(0) {}

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details);

  void AddHandler(LoginHandler* handler);

  void RemoveHandler(LoginHandler* handler);

  void Register(const NotificationSource& source);

  std::list<LoginHandler*> handlers_;

  // The exact number of notifications we receive is depedent on the
  // number of requests that were dispatched and is subject to a
  // number of factors that we don't directly control here.  The
  // values below should only be used qualitatively.
  int auth_needed_count_;
  int auth_supplied_count_;
  int auth_cancelled_count_;

 private:
  NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(LoginPromptBrowserTestObserver);
};

void LoginPromptBrowserTestObserver::Observe(
    NotificationType type,
    const NotificationSource& source,
    const NotificationDetails& details) {
  if (type == NotificationType::AUTH_NEEDED) {
    LoginNotificationDetails* login_details =
        Details<LoginNotificationDetails>(details).ptr();
    AddHandler(login_details->handler());
    auth_needed_count_++;
  } else if (type == NotificationType::AUTH_SUPPLIED) {
    AuthSuppliedLoginNotificationDetails* login_details =
        Details<AuthSuppliedLoginNotificationDetails>(details).ptr();
    RemoveHandler(login_details->handler());
    auth_supplied_count_++;
  } else if (type == NotificationType::AUTH_CANCELLED) {
    LoginNotificationDetails* login_details =
        Details<LoginNotificationDetails>(details).ptr();
    RemoveHandler(login_details->handler());
    auth_cancelled_count_++;
  }
}

void LoginPromptBrowserTestObserver::AddHandler(LoginHandler* handler) {
  std::list<LoginHandler*>::iterator i = std::find(handlers_.begin(),
                                                   handlers_.end(),
                                                   handler);
  EXPECT_TRUE(i == handlers_.end());
  if (i == handlers_.end())
    handlers_.push_back(handler);
}

void LoginPromptBrowserTestObserver::RemoveHandler(LoginHandler* handler) {
  std::list<LoginHandler*>::iterator i = std::find(handlers_.begin(),
                                                   handlers_.end(),
                                                   handler);
  EXPECT_TRUE(i != handlers_.end());
  if (i != handlers_.end())
    handlers_.erase(i);
}

void LoginPromptBrowserTestObserver::Register(
    const NotificationSource& source) {
  registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
  registrar_.Add(this, NotificationType::AUTH_SUPPLIED, source);
  registrar_.Add(this, NotificationType::AUTH_CANCELLED, source);
}

template <NotificationType::Type T>
class WindowedNavigationObserver
    : public ui_test_utils::WindowedNotificationObserver {
 public:
  explicit WindowedNavigationObserver(NavigationController* controller)
      : ui_test_utils::WindowedNotificationObserver(
          T, Source<NavigationController>(controller)) {}
};

typedef WindowedNavigationObserver<NotificationType::LOAD_STOP>
    WindowedLoadStopObserver;

typedef WindowedNavigationObserver<NotificationType::AUTH_NEEDED>
    WindowedAuthNeededObserver;

typedef WindowedNavigationObserver<NotificationType::AUTH_CANCELLED>
    WindowedAuthCancelledObserver;

typedef WindowedNavigationObserver<NotificationType::AUTH_SUPPLIED>
    WindowedAuthSuppliedObserver;

const char* kMultiRealmTestPage = "files/login/multi_realm.html";
const int   kMultiRealmTestRealmCount = 2;
const int   kMultiRealmTestResourceCount = 4;

const char* kSingleRealmTestPage = "files/login/single_realm.html";
const int   kSingleRealmTestResourceCount = 6;

// Test handling of resources that require authentication even though
// the page they are included on doesn't.  In this case we should only
// present the minimal number of prompts necessary for successfully
// displaying the page.  First we check whether cancelling works as
// expected.
IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmCancellation) {
  ASSERT_TRUE(test_server()->Start());
  GURL test_page = test_server()->GetURL(kMultiRealmTestPage);

  TabContentsWrapper* contents =
      browser()->GetSelectedTabContentsWrapper();
  ASSERT_TRUE(contents);

  NavigationController* controller = &contents->controller();
  LoginPromptBrowserTestObserver observer;

  observer.Register(Source<NavigationController>(controller));

  WindowedLoadStopObserver load_stop_waiter(controller);

  {
    WindowedAuthNeededObserver auth_needed_waiter(controller);
    browser()->OpenURL(test_page, GURL(), CURRENT_TAB, PageTransition::TYPED);
    auth_needed_waiter.Wait();
  }

  int n_handlers = 0;

  while (n_handlers < kMultiRealmTestRealmCount) {
    WindowedAuthNeededObserver auth_needed_waiter(controller);

    while (!observer.handlers_.empty()) {
      WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
      LoginHandler* handler = *observer.handlers_.begin();

      ASSERT_TRUE(handler);
      n_handlers++;
      handler->CancelAuth();
      auth_cancelled_waiter.Wait();
    }

    if (n_handlers < kMultiRealmTestRealmCount)
      auth_needed_waiter.Wait();
  }

  load_stop_waiter.Wait();

  EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers);
  EXPECT_EQ(0, observer.auth_supplied_count_);
  EXPECT_LT(0, observer.auth_needed_count_);
  EXPECT_LT(0, observer.auth_cancelled_count_);
  EXPECT_TRUE(test_server()->Stop());
}

// Similar to the MultipleRealmCancellation test above, but tests
// whether supplying credentials work as exepcted.
IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmConfirmation) {
  ASSERT_TRUE(test_server()->Start());
  GURL test_page = test_server()->GetURL(kMultiRealmTestPage);

  TabContentsWrapper* contents =
      browser()->GetSelectedTabContentsWrapper();
  ASSERT_TRUE(contents);

  NavigationController* controller = &contents->controller();
  LoginPromptBrowserTestObserver observer;

  observer.Register(Source<NavigationController>(controller));

  WindowedLoadStopObserver load_stop_waiter(controller);
  int n_handlers = 0;

  {
    WindowedAuthNeededObserver auth_needed_waiter(controller);

    browser()->OpenURL(test_page, GURL(), CURRENT_TAB, PageTransition::TYPED);
    auth_needed_waiter.Wait();
  }

  while (n_handlers < kMultiRealmTestRealmCount) {
    WindowedAuthNeededObserver auth_needed_waiter(controller);

    while (!observer.handlers_.empty()) {
      WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
      LoginHandler* handler = *observer.handlers_.begin();

      ASSERT_TRUE(handler);
      n_handlers++;
      SetAuthFor(handler);
      auth_supplied_waiter.Wait();
    }

    if (n_handlers < kMultiRealmTestRealmCount)
      auth_needed_waiter.Wait();
  }

  load_stop_waiter.Wait();

  EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers);
  EXPECT_LT(0, observer.auth_needed_count_);
  EXPECT_LT(0, observer.auth_supplied_count_);
  EXPECT_EQ(0, observer.auth_cancelled_count_);
  EXPECT_TRUE(test_server()->Stop());
}

// Testing for recovery from an incorrect password for the case where
// there are multiple authenticated resources.
// Marked as flaky.  See crbug.com/68860
IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, FLAKY_IncorrectConfirmation) {
  ASSERT_TRUE(test_server()->Start());
  GURL test_page = test_server()->GetURL(kSingleRealmTestPage);

  TabContentsWrapper* contents =
      browser()->GetSelectedTabContentsWrapper();
  ASSERT_TRUE(contents);

  NavigationController* controller = &contents->controller();
  LoginPromptBrowserTestObserver observer;

  observer.Register(Source<NavigationController>(controller));

  WindowedLoadStopObserver load_stop_waiter(controller);

  {
    WindowedAuthNeededObserver auth_needed_waiter(controller);
    browser()->OpenURL(test_page, GURL(), CURRENT_TAB, PageTransition::TYPED);
    auth_needed_waiter.Wait();
  }

  EXPECT_FALSE(observer.handlers_.empty());

  if (!observer.handlers_.empty()) {
    WindowedAuthNeededObserver auth_needed_waiter(controller);
    WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
    LoginHandler* handler = *observer.handlers_.begin();

    ASSERT_TRUE(handler);
    handler->SetAuth(bad_username_, bad_password_);
    auth_supplied_waiter.Wait();

    // The request should be retried after the incorrect password is
    // supplied.  This should result in a new AUTH_NEEDED notification
    // for the same realm.
    auth_needed_waiter.Wait();
  }

  int n_handlers = 0;

  while (n_handlers < 1) {
    WindowedAuthNeededObserver auth_needed_waiter(controller);

    while (!observer.handlers_.empty()) {
      WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
      LoginHandler* handler = *observer.handlers_.begin();

      ASSERT_TRUE(handler);
      n_handlers++;
      SetAuthFor(handler);
      auth_supplied_waiter.Wait();
    }

    if (n_handlers < 1)
      auth_needed_waiter.Wait();
  }

  load_stop_waiter.Wait();

  // The single realm test has only one realm, and thus only one login
  // prompt.
  EXPECT_EQ(1, n_handlers);
  EXPECT_LT(0, observer.auth_needed_count_);
  EXPECT_LT(0, observer.auth_supplied_count_);
  EXPECT_EQ(0, observer.auth_cancelled_count_);
  EXPECT_TRUE(test_server()->Stop());
}
} // namespace
<commit_msg>Additional logging for LoginPromptBrowserTest.IncorrectConfirmation<commit_after>// 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 <algorithm>
#include <list>
#include <map>

#include "chrome/browser/browser_thread.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/login/login_prompt.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/in_process_browser_test.h"
#include "chrome/test/ui_test_utils.h"
#include "net/base/auth.h"

namespace {

class LoginPromptBrowserTest : public InProcessBrowserTest {
 public:
  LoginPromptBrowserTest()
      : bad_password_(L"incorrect"), bad_username_(L"nouser") {
    set_show_window(true);

    auth_map_[L"foo"] = AuthInfo(L"testuser", L"foopassword");
    auth_map_[L"bar"] = AuthInfo(L"testuser", L"barpassword");
  }

 protected:
  void SetAuthFor(LoginHandler* handler);

  struct AuthInfo {
    std::wstring username_;
    std::wstring password_;

    AuthInfo() {}

    AuthInfo(const std::wstring username,
             const std::wstring password)
        : username_(username), password_(password) {}
  };

  std::map<std::wstring, AuthInfo> auth_map_;
  std::wstring bad_password_;
  std::wstring bad_username_;
};

void LoginPromptBrowserTest::SetAuthFor(LoginHandler* handler) {
  const net::AuthChallengeInfo* challenge = handler->auth_info();

  ASSERT_TRUE(challenge);
  std::map<std::wstring, AuthInfo>::iterator i =
      auth_map_.find(challenge->realm);
  EXPECT_TRUE(auth_map_.end() != i);
  if (i != auth_map_.end()) {
    const AuthInfo& info = i->second;
    handler->SetAuth(info.username_, info.password_);
  }
}

// Maintains a set of LoginHandlers that are currently active and
// keeps a count of the notifications that were observed.
class LoginPromptBrowserTestObserver : public NotificationObserver {
 public:
  LoginPromptBrowserTestObserver()
      : auth_needed_count_(0),
        auth_supplied_count_(0),
        auth_cancelled_count_(0) {}

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details);

  void AddHandler(LoginHandler* handler);

  void RemoveHandler(LoginHandler* handler);

  void Register(const NotificationSource& source);

  std::list<LoginHandler*> handlers_;

  // The exact number of notifications we receive is depedent on the
  // number of requests that were dispatched and is subject to a
  // number of factors that we don't directly control here.  The
  // values below should only be used qualitatively.
  int auth_needed_count_;
  int auth_supplied_count_;
  int auth_cancelled_count_;

 private:
  NotificationRegistrar registrar_;

  DISALLOW_COPY_AND_ASSIGN(LoginPromptBrowserTestObserver);
};

void LoginPromptBrowserTestObserver::Observe(
    NotificationType type,
    const NotificationSource& source,
    const NotificationDetails& details) {
  if (type == NotificationType::AUTH_NEEDED) {
    LoginNotificationDetails* login_details =
        Details<LoginNotificationDetails>(details).ptr();
    AddHandler(login_details->handler());
    auth_needed_count_++;
  } else if (type == NotificationType::AUTH_SUPPLIED) {
    AuthSuppliedLoginNotificationDetails* login_details =
        Details<AuthSuppliedLoginNotificationDetails>(details).ptr();
    RemoveHandler(login_details->handler());
    auth_supplied_count_++;
  } else if (type == NotificationType::AUTH_CANCELLED) {
    LoginNotificationDetails* login_details =
        Details<LoginNotificationDetails>(details).ptr();
    RemoveHandler(login_details->handler());
    auth_cancelled_count_++;
  }
}

void LoginPromptBrowserTestObserver::AddHandler(LoginHandler* handler) {
  std::list<LoginHandler*>::iterator i = std::find(handlers_.begin(),
                                                   handlers_.end(),
                                                   handler);
  EXPECT_TRUE(i == handlers_.end());
  if (i == handlers_.end())
    handlers_.push_back(handler);
}

void LoginPromptBrowserTestObserver::RemoveHandler(LoginHandler* handler) {
  std::list<LoginHandler*>::iterator i = std::find(handlers_.begin(),
                                                   handlers_.end(),
                                                   handler);
  EXPECT_TRUE(i != handlers_.end());
  if (i != handlers_.end())
    handlers_.erase(i);
}

void LoginPromptBrowserTestObserver::Register(
    const NotificationSource& source) {
  registrar_.Add(this, NotificationType::AUTH_NEEDED, source);
  registrar_.Add(this, NotificationType::AUTH_SUPPLIED, source);
  registrar_.Add(this, NotificationType::AUTH_CANCELLED, source);
}

template <NotificationType::Type T>
class WindowedNavigationObserver
    : public ui_test_utils::WindowedNotificationObserver {
 public:
  explicit WindowedNavigationObserver(NavigationController* controller)
      : ui_test_utils::WindowedNotificationObserver(
          T, Source<NavigationController>(controller)) {}
};

typedef WindowedNavigationObserver<NotificationType::LOAD_STOP>
    WindowedLoadStopObserver;

typedef WindowedNavigationObserver<NotificationType::AUTH_NEEDED>
    WindowedAuthNeededObserver;

typedef WindowedNavigationObserver<NotificationType::AUTH_CANCELLED>
    WindowedAuthCancelledObserver;

typedef WindowedNavigationObserver<NotificationType::AUTH_SUPPLIED>
    WindowedAuthSuppliedObserver;

const char* kMultiRealmTestPage = "files/login/multi_realm.html";
const int   kMultiRealmTestRealmCount = 2;
const int   kMultiRealmTestResourceCount = 4;

const char* kSingleRealmTestPage = "files/login/single_realm.html";
const int   kSingleRealmTestResourceCount = 6;

// Test handling of resources that require authentication even though
// the page they are included on doesn't.  In this case we should only
// present the minimal number of prompts necessary for successfully
// displaying the page.  First we check whether cancelling works as
// expected.
IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmCancellation) {
  ASSERT_TRUE(test_server()->Start());
  GURL test_page = test_server()->GetURL(kMultiRealmTestPage);

  TabContentsWrapper* contents =
      browser()->GetSelectedTabContentsWrapper();
  ASSERT_TRUE(contents);

  NavigationController* controller = &contents->controller();
  LoginPromptBrowserTestObserver observer;

  observer.Register(Source<NavigationController>(controller));

  WindowedLoadStopObserver load_stop_waiter(controller);

  {
    WindowedAuthNeededObserver auth_needed_waiter(controller);
    browser()->OpenURL(test_page, GURL(), CURRENT_TAB, PageTransition::TYPED);
    auth_needed_waiter.Wait();
  }

  int n_handlers = 0;

  while (n_handlers < kMultiRealmTestRealmCount) {
    WindowedAuthNeededObserver auth_needed_waiter(controller);

    while (!observer.handlers_.empty()) {
      WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
      LoginHandler* handler = *observer.handlers_.begin();

      ASSERT_TRUE(handler);
      n_handlers++;
      handler->CancelAuth();
      auth_cancelled_waiter.Wait();
    }

    if (n_handlers < kMultiRealmTestRealmCount)
      auth_needed_waiter.Wait();
  }

  load_stop_waiter.Wait();

  EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers);
  EXPECT_EQ(0, observer.auth_supplied_count_);
  EXPECT_LT(0, observer.auth_needed_count_);
  EXPECT_LT(0, observer.auth_cancelled_count_);
  EXPECT_TRUE(test_server()->Stop());
}

// Similar to the MultipleRealmCancellation test above, but tests
// whether supplying credentials work as exepcted.
IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmConfirmation) {
  ASSERT_TRUE(test_server()->Start());
  GURL test_page = test_server()->GetURL(kMultiRealmTestPage);

  TabContentsWrapper* contents =
      browser()->GetSelectedTabContentsWrapper();
  ASSERT_TRUE(contents);

  NavigationController* controller = &contents->controller();
  LoginPromptBrowserTestObserver observer;

  observer.Register(Source<NavigationController>(controller));

  WindowedLoadStopObserver load_stop_waiter(controller);
  int n_handlers = 0;

  {
    WindowedAuthNeededObserver auth_needed_waiter(controller);

    browser()->OpenURL(test_page, GURL(), CURRENT_TAB, PageTransition::TYPED);
    auth_needed_waiter.Wait();
  }

  while (n_handlers < kMultiRealmTestRealmCount) {
    WindowedAuthNeededObserver auth_needed_waiter(controller);

    while (!observer.handlers_.empty()) {
      WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
      LoginHandler* handler = *observer.handlers_.begin();

      ASSERT_TRUE(handler);
      n_handlers++;
      SetAuthFor(handler);
      auth_supplied_waiter.Wait();
    }

    if (n_handlers < kMultiRealmTestRealmCount)
      auth_needed_waiter.Wait();
  }

  load_stop_waiter.Wait();

  EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers);
  EXPECT_LT(0, observer.auth_needed_count_);
  EXPECT_LT(0, observer.auth_supplied_count_);
  EXPECT_EQ(0, observer.auth_cancelled_count_);
  EXPECT_TRUE(test_server()->Stop());
}

// Testing for recovery from an incorrect password for the case where
// there are multiple authenticated resources.
// Marked as flaky.  See http://crbug.com/69266 and http://crbug.com/68860
// TODO(asanka): Remove logging when timeout issues are resolved.
IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, FLAKY_IncorrectConfirmation) {
  ASSERT_TRUE(test_server()->Start());
  GURL test_page = test_server()->GetURL(kSingleRealmTestPage);

  TabContentsWrapper* contents =
      browser()->GetSelectedTabContentsWrapper();
  ASSERT_TRUE(contents);

  NavigationController* controller = &contents->controller();
  LoginPromptBrowserTestObserver observer;

  observer.Register(Source<NavigationController>(controller));

  WindowedLoadStopObserver load_stop_waiter(controller);

  LOG(INFO) <<
      "Begin test run "
      "(tracing for potential hang. crbug.com/69266)";
  {
    WindowedAuthNeededObserver auth_needed_waiter(controller);
    browser()->OpenURL(test_page, GURL(), CURRENT_TAB, PageTransition::TYPED);
    LOG(INFO) << "Waiting for initial AUTH_NEEDED";
    auth_needed_waiter.Wait();
  }

  EXPECT_FALSE(observer.handlers_.empty());

  if (!observer.handlers_.empty()) {
    WindowedAuthNeededObserver auth_needed_waiter(controller);
    WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
    LoginHandler* handler = *observer.handlers_.begin();

    ASSERT_TRUE(handler);
    handler->SetAuth(bad_username_, bad_password_);
    LOG(INFO) << "Waiting for initial AUTH_SUPPLIED";
    auth_supplied_waiter.Wait();

    // The request should be retried after the incorrect password is
    // supplied.  This should result in a new AUTH_NEEDED notification
    // for the same realm.
    LOG(INFO) << "Waiting for secondary AUTH_NEEDED";
    auth_needed_waiter.Wait();
  }

  int n_handlers = 0;

  while (n_handlers < 1) {
    WindowedAuthNeededObserver auth_needed_waiter(controller);

    while (!observer.handlers_.empty()) {
      WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
      LoginHandler* handler = *observer.handlers_.begin();

      ASSERT_TRUE(handler);
      n_handlers++;
      SetAuthFor(handler);
      LOG(INFO) << "Waiting for secondary AUTH_SUPPLIED";
      auth_supplied_waiter.Wait();
    }

    if (n_handlers < 1) {
      LOG(INFO) << "Waiting for additional AUTH_NEEDED";
      auth_needed_waiter.Wait();
    }
  }

  // The single realm test has only one realm, and thus only one login
  // prompt.
  EXPECT_EQ(1, n_handlers);
  EXPECT_LT(0, observer.auth_needed_count_);
  EXPECT_EQ(0, observer.auth_cancelled_count_);
  EXPECT_EQ(observer.auth_needed_count_, observer.auth_supplied_count_);
  LOG(INFO) << "Waiting for LOAD_STOP";
  load_stop_waiter.Wait();
  EXPECT_TRUE(test_server()->Stop());
  LOG(INFO) << "Done with test";
}
}  // namespace
<|endoftext|>


<commit_before>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.2 $  $Author: trey $  $Date: 2006-04-07 20:15:40 $

 @file    solveMDP.cc
 @brief   No brief

 Copyright (c) 2002-2006, Trey Smith.  All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:

 * The software may not be sold or incorporated into a commercial
   product without specific prior written permission.
 * The above copyright notice and this permission notice shall be
   included in all copies or substantial portions of the software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 ***************************************************************************/

#include <assert.h>
#include <sys/time.h>
#include <getopt.h>

#include <iostream>
#include <fstream>

#include "Interleave.h"
#include "stdinInterface.h"

// search strategies
#include "RTDP.h"
#include "LRTDP.h"
#include "HDP.h"
#include "FRTDP.h"

// problem types
#include "RaceTrack.h"
#include "Pomdp.h"
#include "MDPSim.h"
#include "PomdpSim.h"

// value function representations
#include "PointBounds.h"
#include "ConvexBounds.h"

// initialization code
#include "RelaxUBInitializer.h"

using namespace std;
using namespace MatrixUtils;
using namespace zmdp;

struct SolveMDPParams {
  int strategy;
  int probType;
  int valueRepr;
  char *probName;
  int minOrder;
  int maxOrder;
  bool useFastParser;
  int numIterations;
  bool past_options;
  double targetPrecision;
  double minWait;
  bool useInterleave;
  bool useHeuristic;
};

struct EnumEntry {
  const char* key;
  int val;
};

enum StrategiesEnum {
  S_RTDP,
  S_LRTDP,
  S_HDP,
  S_FRTDP
};

EnumEntry strategiesG[] = {
  {"rtdp",  S_RTDP},
  {"lrtdp", S_LRTDP},
  {"hdp",   S_HDP},
  {"frtdp", S_FRTDP},
  {NULL, -1}
};

enum ProbTypesEnum {
  T_RACETRACK,
  T_POMDP
};

EnumEntry probTypesG[] = {
  {"racetrack", T_RACETRACK},
  {"pomdp",     T_POMDP},
  {NULL, -1}
};

enum ValueReprsEnum {
  V_POINT,
  V_CONVEX
};

EnumEntry valueReprsG[] = {
  {"point",  V_POINT},
  {"convex", V_CONVEX},
  {NULL, -1}
};

void usage(const char* cmdName);

int getEnum(const char* key, EnumEntry* table, const char* cmdName, char opt)
{
  EnumEntry* i = table;
  for (; NULL != i->key; i++) {
    if (0 == strcmp(i->key, key)) {
      return i->val;
    }
  }
  fprintf(stderr, "ERROR: invalid parameter %s for option -%c\n\n", key, opt);
  usage(cmdName);
  exit(EXIT_FAILURE);
}

bool endsWith(const std::string& s,
	      const std::string& suffix)
{
  if (s.size() < suffix.size()) return false;
  return (s.substr(s.size() - suffix.size()) == suffix);
}

void testBatchIncremental(const SolveMDPParams& p)
{

  cout << "CFLAGS = " << CFLAGS << endl;

  MDP* problem;
  AbstractSim* sim;
  switch (p.probType) {
  case T_RACETRACK:
    problem = new RaceTrack(p.probName);
    sim = new MDPSim(problem);
    break;
  case T_POMDP:
    problem = new Pomdp(p.probName, p.useFastParser);
    sim = new PomdpSim((Pomdp*) problem);
    break;
  default:
    assert(0); // never reach this point
  }

  // this is a bit of a hack...
  bool useLowerBound;
  switch (p.strategy) {
  case S_RTDP:
  case S_LRTDP:
    useLowerBound = false;
    break;
  case S_HDP:
  case S_FRTDP:
    useLowerBound = true;
    break;
  default:
    assert(0); // never reach this point
  };

  IncrementalBounds* bounds;
  PointBounds* pb;
  switch (p.valueRepr) {
  case V_POINT:
    pb = new PointBounds();
    AbstractBound* initUpperBound;
    if (p.useHeuristic && T_POMDP != p.probType) {
      initUpperBound = new RelaxUBInitializer(problem);
    } else {
      initUpperBound = problem->newUpperBound();
    }
    pb->setBounds(problem->newLowerBound(), initUpperBound);
    bounds = pb;
    break;
  case V_CONVEX:
    bounds = new ConvexBounds(useLowerBound);
    break;
  default:
    assert(0); // never reach this point
  };

  Solver* solver;
  switch (p.strategy) {
  case S_RTDP:
    solver = new RTDP();
    break;
  case S_LRTDP:
    solver = new LRTDP();
   break;
  case S_HDP:
    solver = new HDP();
    break;
  case S_FRTDP:
    solver = new FRTDP();
    break;
  default:
    assert(0); // never reach this point
  };
  ((RTDPCore*) solver)->setBounds(bounds);

  Interleave x;
  if (p.useInterleave) {
    x.interleave(/* numIterations = */ p.numIterations,
		 sim, *solver,
		 /* numSteps = */ 251,
		 /* targetPrecision = */ p.targetPrecision,
		 /* minWait = */ p.minWait,
		 /* outFileName = */ "scatter.plot",
		 /* boundsFileNameFmt = */ "plots/bounds%04d.plot",
		 /* simFileNameFmt = */ "plots/sim%04d.plot");
  } else {
    x.batchTestIncremental(/* numIterations = */ p.numIterations,
			   sim, *solver,
			   /* numSteps = */ 251,
			   /* targetPrecision = */ p.targetPrecision,
			   /* minOrder = */ p.minOrder,
			   /* maxOrder = */ p.maxOrder,
			   /* ticksPerOrder = */ 10,
			   /* outFileName = */ "inc.plot",
			   /* boundsFileName = */ "bounds.plot",
			   /* simFileName = */ "sim.plot");
  }

  x.printRewards();
}

void usage(const char* cmdName) {
  cerr <<
    "usage: " << cmdName << " OPTIONS <model>\n"
    "\n"
    "Main flags:\n"
    "  -s or --search         Specifies search strategy. Valid choices:\n"
    "                           rtdp, lrtdp, hdp, frtdp [default: frtdp]\n"
    "  -t or --type           Specifies problem type. Valid choices:\n"
    "                           racetrack, pomdp [default: infer from model filename]\n"
    "  -v or --value          Specifies value function representation. Valid choices\n"
    "                         depend on problem type. With -t pomdp, choices are:\n"
    "                           point, convex [default: convex]\n"
    "                         For other problem types, choices are:\n"
    "                           point [default: point]\n"
    "\n"
    "Other flags:\n"
    "  -h or --help           Print this help\n"
    "  --version              Print version information (CFLAGS used at compilation)\n"
    "  -f or --fast           Use fast (but very picky) alternate POMDP parser\n"
    "  -i or --iterations     Set number of simulation iterations at each policy\n"
    "                           evaluation epoch [default: 100]\n"
    "  -p or --precision      Set target precision in solution quality; run ends when\n"
    "                           target is reached [default: 1e-3]\n"
    "  --weak-heuristic       Avoid spending time generating a good upper bound heuristic\n"
    "                           (only valid for some problems, interpretation depends on\n"
    "                            the problem; e.g. sets h_U = 0 for racetrack)\n"
    "  --min-eval             If minEval=k, start logging policy evaluation epochs\n"
    "                           after 10^k seconds of policy improvement [default: 0]\n"
    "  --max-eval             If maxEval=k, run ends after at most 10^k seconds of policy\n"
    "                           improvement [default: 3]\n"
    "\n"
    "Experimental flags (you probably don't want to use these):\n"
    "  --interleave           Test planner in interleaved mode\n"
    "  -w or --min-wait       Set minimum planning time between actions (for interleaving)\n"
    "\n"
    "Examples:\n"
    "  " << cmdName << " RockSample_4_4.pomdp\n"
    "  " << cmdName << " large-b.racetrack\n"
    "  " << cmdName << " --min-eval -1 RockSample_4_4.pomdp\n"
    "  " << cmdName << " --search lrtdp --value point RockSample_4_4.pomdp\n"
    "  " << cmdName << " -f RockSample_5_7.pomdp\n"
    "\n"
;
  exit(-1);
}

int main(int argc, char **argv) {
  init_matrix_utils();

  static char shortOptions[] = "hs:t:v:fi:p:w:";
  static struct option longOptions[]={
    {"help",          0,NULL,'h'},
    {"search",        1,NULL,'s'},
    {"type",          1,NULL,'t'},
    {"value",         1,NULL,'v'},
    {"version",       0,NULL,'V'},
    {"fast",          0,NULL,'f'},
    {"iterations",    1,NULL,'i'},
    {"precision",     1,NULL,'p'},
    {"weak-heuristic",0,NULL,'W'},
    {"min-eval",      1,NULL,'X'},
    {"max-eval",      1,NULL,'Y'},
    {"interleave",    1,NULL,'I'},
    {"min-wait",      1,NULL,'w'},
    {NULL,0,0,0}
  };

  SolveMDPParams p;
  p.strategy = S_FRTDP;
  p.probType = -1;
  p.valueRepr = -1;
  p.probName = NULL;
  p.minOrder = -999;
  p.maxOrder = -999;
  p.useFastParser = false;
  p.numIterations = 100;
  p.targetPrecision = 1e-3;
  p.minWait = 0;
  p.useInterleave = false;
  p.useHeuristic = true;

  while (1) {
    char optchar = getopt_long(argc,argv,shortOptions,longOptions,NULL);
    if (optchar == -1) break;

    switch (optchar) {
    case 'h': // help
      usage(argv[0]);
      break;
    case 's': // search
      p.strategy = getEnum(optarg, strategiesG, argv[0], optchar);
      break;
    case 't': // type
      p.probType = getEnum(optarg, probTypesG, argv[0], optchar);
      break;
    case 'v': // value
      p.valueRepr = getEnum(optarg, valueReprsG, argv[0], optchar);
      break;
    case 'V': // version
      cout << "CFLAGS = " << CFLAGS << endl;
      exit(EXIT_SUCCESS);
      break;
    case 'f': // fast
      p.useFastParser = true;
      break;
    case 'i': // iterations
      p.numIterations = atoi(optarg);
      break;
    case 'p': // precision
      p.targetPrecision = atof(optarg);
      break;
    case 'W': // weak-heuristic
      p.useHeuristic = false;
      break;
    case 'X': // min-eval
      p.minOrder = atoi(optarg);
      break;
    case 'Y': // max-eval
      p.maxOrder = atoi(optarg);
      break;
    case 'I': // interleave
      p.useInterleave = true;
      break;
    case 'w': // min-wait
      p.minWait = atof(optarg);
      break;

    case '?': // unknown option
    case ':': // option with missing parameter
      // getopt() prints an informative error message
      cerr << endl;
      usage(argv[0]);
      break;
    default:
      abort(); // never reach this point
    }
  }
  if (argc-optind != 1) {
    cerr << "ERROR: wrong number of arguments (should be 1)" << endl << endl;
    usage(argv[0]);
  }

  p.probName = argv[optind++];

  // fill in default and inferred values
  if (-1 == p.probType) {
    if (endsWith(p.probName, ".racetrack")) {
      p.probType = T_RACETRACK;
    } else if (endsWith(p.probName, ".pomdp")) {
      p.probType = T_POMDP;
    } else {
      fprintf(stderr, "ERROR: couldn't infer problem type from problem filename %s\n\n",
	      p.probName);
      usage(argv[0]);
      exit(EXIT_FAILURE);
    }
  }
  if (-1 == p.valueRepr) {
    if (T_POMDP == p.probType) {
      p.valueRepr = V_CONVEX;
    } else {
      p.valueRepr = V_POINT;
    }
  }
  if (-999 == p.minOrder) {
    p.minOrder = 0; // default
  }
  if (-999 == p.maxOrder) {
    p.maxOrder = 3; // default
  }

  cerr << "done arg parsing" << endl;

  testBatchIncremental(p);

  // signal we are done
  FILE *fp = fopen("/tmp/solveMDP_done", "w");
  fprintf(fp, "success\n");
  fclose(fp);

  return 0;
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 * Revision 1.1  2006/04/07 19:40:41  trey
 * switched back to a unified binary for all algorithms, turns out it cuts down on code maintenance
 *
 *
 ***************************************************************************/
<commit_msg>added --lower-bound and --upper-bound args<commit_after>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.3 $  $Author: trey $  $Date: 2006-04-10 20:27:05 $

 @file    solveMDP.cc
 @brief   No brief

 Copyright (c) 2002-2006, Trey Smith.  All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:

 * The software may not be sold or incorporated into a commercial
   product without specific prior written permission.
 * The above copyright notice and this permission notice shall be
   included in all copies or substantial portions of the software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 ***************************************************************************/

#include <assert.h>
#include <sys/time.h>
#include <getopt.h>

#include <iostream>
#include <fstream>

#include "Interleave.h"
#include "stdinInterface.h"

// search strategies
#include "RTDP.h"
#include "LRTDP.h"
#include "HDP.h"
#include "FRTDP.h"

// problem types
#include "RaceTrack.h"
#include "Pomdp.h"
#include "MDPSim.h"
#include "PomdpSim.h"

// value function representations
#include "PointBounds.h"
#include "ConvexBounds.h"

// initialization code
#include "RelaxUBInitializer.h"

using namespace std;
using namespace MatrixUtils;
using namespace zmdp;

struct SolveMDPParams {
  int strategy;
  int probType;
  int valueRepr;
  char *probName;
  int minOrder;
  int maxOrder;
  bool useFastParser;
  int numIterations;
  bool past_options;
  double targetPrecision;
  double minWait;
  bool useInterleave;
  bool useHeuristic;
  bool forceLowerBound;
  bool forceUpperBoundActionSelection;
};

struct EnumEntry {
  const char* key;
  int val;
};

enum StrategiesEnum {
  S_RTDP,
  S_LRTDP,
  S_HDP,
  S_FRTDP
};

EnumEntry strategiesG[] = {
  {"rtdp",  S_RTDP},
  {"lrtdp", S_LRTDP},
  {"hdp",   S_HDP},
  {"frtdp", S_FRTDP},
  {NULL, -1}
};

enum ProbTypesEnum {
  T_RACETRACK,
  T_POMDP
};

EnumEntry probTypesG[] = {
  {"racetrack", T_RACETRACK},
  {"pomdp",     T_POMDP},
  {NULL, -1}
};

enum ValueReprsEnum {
  V_POINT,
  V_CONVEX
};

EnumEntry valueReprsG[] = {
  {"point",  V_POINT},
  {"convex", V_CONVEX},
  {NULL, -1}
};

void usage(const char* cmdName);

int getEnum(const char* key, EnumEntry* table, const char* cmdName, char opt)
{
  EnumEntry* i = table;
  for (; NULL != i->key; i++) {
    if (0 == strcmp(i->key, key)) {
      return i->val;
    }
  }
  fprintf(stderr, "ERROR: invalid parameter %s for option -%c\n\n", key, opt);
  usage(cmdName);
  exit(EXIT_FAILURE);
}

bool endsWith(const std::string& s,
	      const std::string& suffix)
{
  if (s.size() < suffix.size()) return false;
  return (s.substr(s.size() - suffix.size()) == suffix);
}

void testBatchIncremental(const SolveMDPParams& p)
{
  init_matrix_utils();

  MDP* problem;
  AbstractSim* sim;
  switch (p.probType) {
  case T_RACETRACK:
    problem = new RaceTrack(p.probName);
    sim = new MDPSim(problem);
    break;
  case T_POMDP:
    problem = new Pomdp(p.probName, p.useFastParser);
    sim = new PomdpSim((Pomdp*) problem);
    break;
  default:
    assert(0); // never reach this point
  }

  Solver* solver;
  switch (p.strategy) {
  case S_RTDP:
    solver = new RTDP();
    break;
  case S_LRTDP:
    solver = new LRTDP();
   break;
  case S_HDP:
    solver = new HDP();
    break;
  case S_FRTDP:
    solver = new FRTDP();
    break;
  default:
    assert(0); // never reach this point
  };

  bool keepLowerBound = p.forceLowerBound
    || ((RTDPCore *) solver)->getUseLowerBound();
  IncrementalBounds* bounds;
  switch (p.valueRepr) {
  case V_POINT:
    PointBounds* pb;
    AbstractBound *initLowerBound, *initUpperBound;

    pb = new PointBounds();
    initLowerBound = keepLowerBound ? problem->newLowerBound() : NULL;
    if (p.useHeuristic && T_POMDP != p.probType) {
      initUpperBound = new RelaxUBInitializer(problem);
    } else {
      initUpperBound = problem->newUpperBound();
    }
    pb->setBounds(initLowerBound, initUpperBound, p.forceUpperBoundActionSelection);
    bounds = pb;
    break;
  case V_CONVEX:
    bounds = new ConvexBounds(keepLowerBound, p.forceUpperBoundActionSelection);
    break;
  default:
    assert(0); // never reach this point
  };
  ((RTDPCore*) solver)->setBounds(bounds);

  Interleave x;
  if (p.useInterleave) {
    x.interleave(/* numIterations = */ p.numIterations,
		 sim, *solver,
		 /* numSteps = */ 251,
		 /* targetPrecision = */ p.targetPrecision,
		 /* minWait = */ p.minWait,
		 /* outFileName = */ "scatter.plot",
		 /* boundsFileNameFmt = */ "plots/bounds%04d.plot",
		 /* simFileNameFmt = */ "plots/sim%04d.plot");
  } else {
    x.batchTestIncremental(/* numIterations = */ p.numIterations,
			   sim, *solver,
			   /* numSteps = */ 251,
			   /* targetPrecision = */ p.targetPrecision,
			   /* minOrder = */ p.minOrder,
			   /* maxOrder = */ p.maxOrder,
			   /* ticksPerOrder = */ 10,
			   /* outFileName = */ "inc.plot",
			   /* boundsFileName = */ "bounds.plot",
			   /* simFileName = */ "sim.plot");
  }

  x.printRewards();
}

void usage(const char* cmdName) {
  cerr <<
    "usage: " << cmdName << " OPTIONS <model>\n"
    "\n"
    "Main flags:\n"
    "  -s or --search         Specifies search strategy. Valid choices:\n"
    "                           rtdp, lrtdp, hdp, frtdp [default: frtdp]\n"
    "  -t or --type           Specifies problem type. Valid choices:\n"
    "                           racetrack, pomdp [default: infer from model filename]\n"
    "  -v or --value          Specifies value function representation. Valid choices\n"
    "                         depend on problem type. With -t pomdp, choices are:\n"
    "                           point, convex [default: convex]\n"
    "                         For other problem types, choices are:\n"
    "                           point [default: point]\n"
    "\n"
    "Other flags:\n"
    "  -h or --help           Print this help\n"
    "  --version              Print version information (CFLAGS used at compilation)\n"
    "  -f or --fast           Use fast (but very picky) alternate POMDP parser\n"
    "  -i or --iterations     Set number of simulation iterations at each policy\n"
    "                           evaluation epoch [default: 100]\n"
    "  -p or --precision      Set target precision in solution quality; run ends when\n"
    "                           target is reached [default: 1e-3]\n"
    "  --weak-heuristic       Avoid spending time generating a good upper bound heuristic\n"
    "                           (only valid for some problems, interpretation depends on\n"
    "                            the problem; e.g. sets h_U = 0 for racetrack)\n"
    "  --lower-bound          Forces solveMDP to maintain a lower bound and use it for\n"
    "                           action selection, even if it is not used during search.\n"
    "  --upper-bound          Forces solveMDP to use the upper bound for action selection\n"
    "                           (normally the lower bound is used if it is available).\n"
    "  --min-eval             If minEval=k, start logging policy evaluation epochs\n"
    "                           after 10^k seconds of policy improvement [default: 0]\n"
    "  --max-eval             If maxEval=k, run ends after at most 10^k seconds of policy\n"
    "                           improvement [default: 3]\n"
    "\n"
    "Experimental flags (you probably don't want to use these):\n"
    "  --interleave           Test planner in interleaved mode\n"
    "  -w or --min-wait       Set minimum planning time between actions (for interleaving)\n"
    "\n"
    "Examples:\n"
    "  " << cmdName << " RockSample_4_4.pomdp\n"
    "  " << cmdName << " large-b.racetrack\n"
    "  " << cmdName << " --min-eval -1 RockSample_4_4.pomdp\n"
    "  " << cmdName << " --search lrtdp --value point RockSample_4_4.pomdp\n"
    "  " << cmdName << " -f RockSample_5_7.pomdp\n"
    "\n"
;
  exit(-1);
}

int main(int argc, char **argv) {
  static char shortOptions[] = "hs:t:v:fi:p:w:";
  static struct option longOptions[]={
    {"help",          0,NULL,'h'},
    {"search",        1,NULL,'s'},
    {"type",          1,NULL,'t'},
    {"value",         1,NULL,'v'},
    {"version",       0,NULL,'V'},
    {"fast",          0,NULL,'f'},
    {"iterations",    1,NULL,'i'},
    {"precision",     1,NULL,'p'},
    {"weak-heuristic",0,NULL,'W'},
    {"lower-bound",   0,NULL,'L'},
    {"upper-bound",   0,NULL,'U'},
    {"min-eval",      1,NULL,'X'},
    {"max-eval",      1,NULL,'Y'},
    {"interleave",    1,NULL,'I'},
    {"min-wait",      1,NULL,'w'},
    {NULL,0,0,0}
  };

  SolveMDPParams p;
  p.strategy = S_FRTDP;
  p.probType = -1;
  p.valueRepr = -1;
  p.probName = NULL;
  p.minOrder = -999;
  p.maxOrder = -999;
  p.useFastParser = false;
  p.numIterations = 100;
  p.targetPrecision = 1e-3;
  p.minWait = 0;
  p.useInterleave = false;
  p.useHeuristic = true;
  p.forceLowerBound = false;
  p.forceUpperBoundActionSelection = false;

  ostringstream outs;
  for (int i=1; i < argc; i++) {
    outs << argv[i] << " ";
  }

  while (1) {
    char optchar = getopt_long(argc,argv,shortOptions,longOptions,NULL);
    if (optchar == -1) break;

    switch (optchar) {
    case 'h': // help
      usage(argv[0]);
      break;
    case 's': // search
      p.strategy = getEnum(optarg, strategiesG, argv[0], optchar);
      break;
    case 't': // type
      p.probType = getEnum(optarg, probTypesG, argv[0], optchar);
      break;
    case 'v': // value
      p.valueRepr = getEnum(optarg, valueReprsG, argv[0], optchar);
      break;
    case 'V': // version
      cout << "CFLAGS = " << CFLAGS << endl;
      exit(EXIT_SUCCESS);
      break;
    case 'f': // fast
      p.useFastParser = true;
      break;
    case 'i': // iterations
      p.numIterations = atoi(optarg);
      break;
    case 'p': // precision
      p.targetPrecision = atof(optarg);
      break;
    case 'W': // weak-heuristic
      p.useHeuristic = false;
      break;
    case 'L': // lower-bound
      p.forceLowerBound = true;
      break;
    case 'U': // upper-bound
      p.forceUpperBoundActionSelection = true;
      break;
    case 'X': // min-eval
      p.minOrder = atoi(optarg);
      break;
    case 'Y': // max-eval
      p.maxOrder = atoi(optarg);
      break;
    case 'I': // interleave
      p.useInterleave = true;
      break;
    case 'w': // min-wait
      p.minWait = atof(optarg);
      break;

    case '?': // unknown option
    case ':': // option with missing parameter
      // getopt() prints an informative error message
      cerr << endl;
      usage(argv[0]);
      break;
    default:
      abort(); // never reach this point
    }
  }
  if (argc-optind != 1) {
    cerr << "ERROR: wrong number of arguments (should be 1)" << endl << endl;
    usage(argv[0]);
  }

  p.probName = argv[optind++];

  // fill in default and inferred values
  if (-1 == p.probType) {
    if (endsWith(p.probName, ".racetrack")) {
      p.probType = T_RACETRACK;
    } else if (endsWith(p.probName, ".pomdp")) {
      p.probType = T_POMDP;
    } else {
      fprintf(stderr, "ERROR: couldn't infer problem type from problem filename %s (use -t option)\n\n",
	      p.probName);
      usage(argv[0]);
      exit(EXIT_FAILURE);
    }
  }
  if (-1 == p.valueRepr) {
    if (T_POMDP == p.probType) {
      p.valueRepr = V_CONVEX;
    } else {
      p.valueRepr = V_POINT;
    }
  }
  if (-999 == p.minOrder) {
    p.minOrder = 0; // default
  }
  if (-999 == p.maxOrder) {
    p.maxOrder = 3; // default
  }

  printf("CFLAGS = %s\n", CFLAGS);
  printf("ARGS = %s\n", outs.str().c_str());
  fflush(stdout);

  testBatchIncremental(p);

  // signal we are done
  FILE *fp = fopen("/tmp/solveMDP_done", "w");
  fprintf(fp, "success\n");
  fclose(fp);

  return 0;
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 * Revision 1.2  2006/04/07 20:15:40  trey
 * solveMDP now uses a strong heuristic by default; improved usage() help
 *
 * Revision 1.1  2006/04/07 19:40:41  trey
 * switched back to a unified binary for all algorithms, turns out it cuts down on code maintenance
 *
 *
 ***************************************************************************/
<|endoftext|>


<commit_before>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
/*
COPYING CONDITIONS NOTICE:

  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation, and provided that the
  following conditions are met:

      * Redistributions of source code must retain this COPYING
        CONDITIONS NOTICE, the COPYRIGHT NOTICE (below), the
        DISCLAIMER (below), the UNIVERSITY PATENT NOTICE (below), the
        PATENT MARKING NOTICE (below), and the PATENT RIGHTS
        GRANT (below).

      * Redistributions in binary form must reproduce this COPYING
        CONDITIONS NOTICE, the COPYRIGHT NOTICE (below), the
        DISCLAIMER (below), the UNIVERSITY PATENT NOTICE (below), the
        PATENT MARKING NOTICE (below), and the PATENT RIGHTS
        GRANT (below) in the documentation and/or other materials
        provided with the distribution.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  02110-1301, USA.

COPYRIGHT NOTICE:

  TokuDB, Tokutek Fractal Tree Indexing Library.
  Copyright (C) 2007-2013 Tokutek, Inc.

DISCLAIMER:

  This program is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.

UNIVERSITY PATENT NOTICE:

  The technology is licensed by the Massachusetts Institute of
  Technology, Rutgers State University of New Jersey, and the Research
  Foundation of State University of New York at Stony Brook under
  United States of America Serial No. 11/760379 and to the patents
  and/or patent applications resulting from it.

PATENT MARKING NOTICE:

  This software is covered by US Patent No. 8,185,551.
  This software is covered by US Patent No. 8,489,638.

PATENT RIGHTS GRANT:

  "THIS IMPLEMENTATION" means the copyrightable works distributed by
  Tokutek as part of the Fractal Tree project.

  "PATENT CLAIMS" means the claims of patents that are owned or
  licensable by Tokutek, both currently or in the future; and that in
  the absence of this license would be infringed by THIS
  IMPLEMENTATION or by using or running THIS IMPLEMENTATION.

  "PATENT CHALLENGE" shall mean a challenge to the validity,
  patentability, enforceability and/or non-infringement of any of the
  PATENT CLAIMS or otherwise opposing any of the PATENT CLAIMS.

  Tokutek hereby grants to you, for the term and geographical scope of
  the PATENT CLAIMS, a non-exclusive, no-charge, royalty-free,
  irrevocable (except as stated in this section) patent license to
  make, have made, use, offer to sell, sell, import, transfer, and
  otherwise run, modify, and propagate the contents of THIS
  IMPLEMENTATION, where such license applies only to the PATENT
  CLAIMS.  This grant does not include claims that would be infringed
  only as a consequence of further modifications of THIS
  IMPLEMENTATION.  If you or your agent or licensee institute or order
  or agree to the institution of patent litigation against any entity
  (including a cross-claim or counterclaim in a lawsuit) alleging that
  THIS IMPLEMENTATION constitutes direct or contributory patent
  infringement, or inducement of patent infringement, then any rights
  granted to you under this License shall terminate as of the date
  such litigation is filed.  If you or your agent or exclusive
  licensee institute or order or agree to the institution of a PATENT
  CHALLENGE, then Tokutek may terminate any rights granted to you
  under this License.
*/

#ident "Copyright (c) 2007-2013 Tokutek Inc.  All rights reserved."
#ident "$Id$"
#include "test.h"
#include <db.h>
#include <sys/stat.h>

#include "checkpoint_test.h"

// Only useful for single threaded testing,
// but can be accessed from checkpoint_callback.
static DICTIONARY test_dictionary = NULL;
static int iter = 0;  // horrible technique, but quicker than putting in extra (this is just test code, not product code)

static void
checkpoint_test_1(uint32_t flags, uint32_t n, int snap_all) {
    if (verbose) {
        printf("%s(%s):%d, n=0x%03x, checkpoint=%01x, flags=0x%05x\n",
               __FILE__, __FUNCTION__, __LINE__,
               n, snap_all, flags);
        fflush(stdout);
    }
    dir_create(TOKU_TEST_FILENAME);
    env_startup(TOKU_TEST_FILENAME, 0, 0);
    int run;
    int r;
    DICTIONARY_S db_control;
    init_dictionary(&db_control, flags, "control");
    DICTIONARY_S db_test;
    init_dictionary(&db_test, flags, "test");
    test_dictionary = &db_test;

    db_startup(&db_test, NULL);
    db_startup(&db_control, NULL);
    const int num_runs = 4;
    for (run = 0; run < num_runs; run++) {
        uint32_t i;
        for (i=0; i < n/2/num_runs; i++) {
            insert_random(db_test.db, db_control.db, NULL);
        }
        snapshot(&db_test, snap_all);
        for (i=0; i < n/2/num_runs; i++) {
            insert_random(db_test.db, NULL, NULL);
        }
        db_replace(TOKU_TEST_FILENAME, &db_test, NULL);
        r = compare_dbs(db_test.db, db_control.db);
	assert(r==0);
    }
    db_shutdown(&db_test);
    db_shutdown(&db_control);
    env_shutdown();
}

static void
checkpoint_test_2(uint32_t flags, uint32_t n) {
    if (verbose) {
        printf("%s(%s):%d, n=0x%03x, checkpoint=%01x, flags=0x%05x\n",
               __FILE__, __FUNCTION__, __LINE__,
               n, 1, flags);
	printf("Verify that inserts done during checkpoint are effective\n");
        fflush(stdout);
    }
    dir_create(TOKU_TEST_FILENAME);
    env_startup(TOKU_TEST_FILENAME, 0, 0);
    int run;
    int r;
    DICTIONARY_S db_control;
    init_dictionary(&db_control, flags, "control");
    DICTIONARY_S db_test;
    init_dictionary(&db_test, flags, "test");
    test_dictionary = &db_test;

    db_startup(&db_test, NULL);
    db_startup(&db_control, NULL);
    const int num_runs = 4;
    for (run = 0; run < num_runs; run++) {
	iter = run;
        uint32_t i;
        for (i=0; i < n/2/num_runs; i++)
            insert_random(db_test.db, db_control.db, NULL);
	r = compare_dbs(db_test.db, db_control.db);
	assert(r==0);
        snapshot(&db_test, true);  // take checkpoint, insert into test db during checkpoint callback
	r = compare_dbs(db_test.db, db_control.db);
	// test and control should be different
	assert(r!=0);
	// now insert same rows into control and they should be same
	insert_n_fixed(db_control.db, NULL, NULL, iter * NUM_FIXED_ROWS, NUM_FIXED_ROWS);
	r = compare_dbs(db_test.db, db_control.db);
	assert(r==0);
    }
    // now close db_test via checkpoint callback (i.e. during checkpoint)
    iter = -1;
    snapshot(&db_test, true);
    db_shutdown(&db_control);
    env_shutdown();
}

// Purpose is to scribble over test db while checkpoint is
// in progress.
static void checkpoint_callback_1(void * extra) {
    DICTIONARY d = *(DICTIONARY*) extra;
    int i;
    char name[MAX_NAME*2];
    fill_name(d, name, sizeof(name));

    if (verbose) {
	printf("checkpoint_callback_1 inserting randomly into %s\n",
	       name);
	fflush(stdout);
    }
    for (i=0; i < 1024; i++)
	insert_random(d->db, NULL, NULL);

}

static void checkpoint_callback_2(void * extra) {
    DICTIONARY d = *(DICTIONARY*) extra;
    assert(d==test_dictionary);
    char name[MAX_NAME*2];
    fill_name(d, name, sizeof(name));

    if (iter >= 0) {
	if (verbose) {
	    printf("checkpoint_callback_2 inserting fixed rows into %s\n",
		   name);
	    fflush(stdout);
	}
	insert_n_fixed(d->db, NULL, NULL, iter * NUM_FIXED_ROWS, NUM_FIXED_ROWS);
    }
    else {
	DICTIONARY_S db_temp;
	init_dictionary(&db_temp, 0, "temp");
	int i;
	if (verbose) {
	    printf("checkpoint_callback_2 closing %s\n",
		   name);
	    fflush(stdout);
	}
	db_shutdown(d);
	if (verbose) {
	    printf("checkpoint_callback_2 opening and closing unrelated dictionary\n");
	    fflush(stdout);
	}
	db_startup(&db_temp, NULL);
	for (i=0; i<1025; i++)
	    insert_random(db_temp.db, NULL, NULL);
	db_shutdown(&db_temp);
    }
}

int
test_main (int argc, char * const argv[]) {
    parse_args(argc, argv);
    uint32_t n;
    int snap;

    n = 0;
    for (snap = 0; snap < 2; snap++) {
        checkpoint_test_1(0, n, snap);
    }
    for (n = 1; n <= 1<<9; n*= 2) {
        for (snap = 0; snap < 2; snap++) {
            checkpoint_test_1(0, n, snap);
        }
    }

    db_env_set_checkpoint_callback(checkpoint_callback_1, &test_dictionary);
    checkpoint_test_1(0,4096,1);
    db_env_set_checkpoint_callback(checkpoint_callback_2, &test_dictionary);
    checkpoint_test_2(0,4096);
    db_env_set_checkpoint_callback(NULL, NULL);

    return 0;
}

#if 0
 checkpoint_1:
   create two dbs, db_test (observed) and db_control (expected)
   loop n times:
     modify both dbs
     checkpoint db_test
     modify db_test only
     copy db_test file (system(cp)) to db_temp
     compare db_temp with db_control
     continue test using db_temp as db_test instead of db_test
     delete old db_test
 checkpoint_2,3 were subsumed into 1.

TODO: Add callback to toku_checkpoint(), called after ydb lock is released.
      (Note, checkpoint_safe_lock is still held.)

 checkpoint_4:
   Callback can do some inserts, guaranteeing that we are testing that inserts
   are done "during" a checkpoint.

 checkpoint_5:
    Callback does unrelated open, close, and related close.

 checkpoint_6:
    Callback triggers a thread that will perform same operations as:
     * checkpoint_4
     * checkpoint_5
     * delete (relevant db)
     * delete (irrelevant db)
     * take checkpoint safe lock using YDB api, do something like insert and release it
    but those operations happen during execution of toku_cachetable_end_checkpoint().

 checkpoint_7
   take atomic operation lock
   perform some inserts
   on another thread, call toku_checkpoint()
   sleep a few seconds
   perform more inserts
   release atomic operations lock
   wait for checkpoint thread to complete
   verify that checkpointed db has all data inserted

#endif
<commit_msg>refs #82, remove checkpoint_1.cc for TokuDB 7.1.0, may bring it back fixed later. That is still open<commit_after><|endoftext|>


<commit_before>#include "factory.h"
#include <chrono>
#include <thread>

void synopsis()
{
    std::cout << "save_frames"
              << "  [  "
              << std::endl << "                "
              << " help " << " | "
              << std::endl << "                "
              << " epiphan [ xvid | h265 [ sdi | dvi ] ] " << " | "
              << std::endl << "                "
              << " file </file/path> [xvid | h265] " << " | "
              << std::endl << "                "
              << " chess xvid | h265   <width> <height> "
              << std::endl << "            "
              << " ]"
              << std::endl;
}

enum TestMode { Epiphan, File, Chessboard };

enum TestMode test_mode = TestMode::Chessboard;
enum gg::Target codec = gg::Target::File_XviD;
std::string codec_string = "xvid", filetype = "avi";
/* following numbers purposefully odd,
 * to test robustness when odd frame
 * width and/or height provided, whereas
 * H265 requires even
 */
int width = 11, height = 7, square_size = 61;
float fps = 20;
int duration = 1; // min
int num_frames = duration * 60 * fps;
int i = 0;
VideoFrame_BGRA frame;
cv::Mat image(height * square_size, width * square_size, CV_8UC4);

void parse_args(int argc, char ** argv)
{
    std::vector<std::string> args;
    for (int i = 0; i < argc; i++)
        args.push_back(std::string(argv[i]));

    if (args[1]=="help")
    {
        synopsis(); exit(0);
    }

    if (args[1]=="epiphan")
    {
        // TODO
        return;
    }

    if (args[1]=="file")
    {
        // TODO
        return;
    }

    if (args[1]=="chess")
    {
        test_mode = TestMode::Chessboard;

        if (argc < 5)
        {
            synopsis(); exit(-1);
        }

        codec_string = args[2];
        width = atoi(argv[3]); height = atoi(argv[4]);
    }

    // health checks
    if (codec_string == "xvid")
        codec = gg::Target::File_XviD;
    else if (codec_string == "h265")
    {
        codec = gg::Target::File_H265;
        filetype = "mp4";
    }
    else
    {
        std::cerr << "Codec " << codec_string << " not recognised" << std::endl;
        synopsis();
        exit(-1);
    }
}

std::string which_file()
{
    std::string filename;
    switch(test_mode)
    {case TestMode::Epiphan:
        // TODO
        break;
    case TestMode::File:
        // TODO
        break;
    case TestMode::Chessboard:
        filename.append("1min_")
                .append(codec_string)
                .append("_colour_chessboard.")
                .append(filetype);
        return filename;
    default:
        std::cerr << "Test mode not set" << std::endl;
        exit(-1);
    }
}

void get_frame(VideoFrame_BGRA & frame)
{
    // Chessboard params
    float elapsed, remaining;
    int red, green, blue, alpha, min_intensity;
    switch(test_mode)
    {
    case TestMode::Chessboard:
        elapsed = (((float) i) / num_frames);
        remaining = 1 - elapsed;
        red = 0, green = 0, blue = 0, alpha = 0;
        min_intensity = 50; // don't want black
        for (int row = 0; row < height; row++)
        {
            green = min_intensity + elapsed * (((float) row) / height) * (255 - min_intensity);
            for (int col = 0; col < width; col++)
            {
                red = min_intensity + elapsed * (((float) col) / width) * (255 - min_intensity);
                blue = min_intensity +
                       remaining *
                            sqrt( ((float)(row*row + col*col))
                                  /
                                  (height*height+width*width) ) * (255 - min_intensity);
                cv::Mat square(square_size, square_size, CV_8UC4, cv::Scalar(blue, green, red, alpha));
                square.copyTo(image(cv::Rect(col * square_size , row * square_size,
                                             square.cols, square.rows)));
            }
        }
        frame.init_from_opencv_mat(image);
        break;
    case TestMode::Epiphan:
        // TODO
        break;
    case TestMode::File:
        // TODO
        break;
    default:
        std::cerr << "Test mode not set" << std::endl;
        exit(-1);
    }
}

auto start = std::chrono::steady_clock::now();
float elapsed;
void time_left()
{
    if (i % 5 == 0)
        std::cout << "Saving frame " << i+1 << " of " << num_frames;

    int left;

    switch(test_mode)
    {
    case TestMode::Chessboard:
        // nop
        break;
    case TestMode::File:
    case TestMode::Epiphan:
        elapsed = std::chrono::duration_cast<std::chrono::seconds>(
                    std::chrono::steady_clock::now() - start).count();
        left = ((float) num_frames - i + 1) * elapsed / (i+1);
        std::cout << " (" << left << " sec. left)";
        break;
    default:
        std::cerr << "Test mode not set" << std::endl;
        exit(-1);
    }

    std::cout << "\r";
}

int main(int argc, char ** argv)
{
    parse_args(argc, argv);

    try
    {
        gg::IVideoTarget * file = gg::Factory::create(codec);
        std::string filename = which_file();
        file->init(filename, fps);
        std::cout << "Saving to file " << filename << std::endl;
        start = std::chrono::steady_clock::now();
        for (i = 0; i < num_frames; i++)
        {
            time_left();
            get_frame(frame);
            file->append(frame);
        }
        file->finalise();
    }
    catch (gg::VideoTargetError & e)
    {
        std::cerr << e.what() << std::endl;
        return -1;
    }

    return 0;
}
<commit_msg>Issue #21: one-for-all app saving frames from file working now<commit_after>#include "factory.h"
#include <chrono>
#include <thread>

void synopsis()
{
    std::cout << "save_frames"
              << "  [  "
              << std::endl << "                "
              << " help " << " | "
              << std::endl << "                "
              << " epiphan [ xvid | h265 [ sdi | dvi ] ] " << " | "
              << std::endl << "                "
              << " file </file/path> [xvid | h265] " << " | "
              << std::endl << "                "
              << " chess xvid | h265   <width> <height> "
              << std::endl << "            "
              << " ]"
              << std::endl;
}

enum TestMode { Epiphan, File, Chessboard };

enum TestMode test_mode = TestMode::Chessboard;
enum gg::Target codec = gg::Target::File_XviD;
std::string codec_string = "xvid", filetype = "avi";
/* following numbers purposefully odd,
 * to test robustness when odd frame
 * width and/or height provided, whereas
 * H265 requires even
 */
int width = 11, height = 7, square_size = 61;
float fps = 20;
int duration = 1; // min
int num_frames = duration * 60 * fps;
int i = 0;
VideoFrame_BGRA frame;
cv::Mat image(height * square_size, width * square_size, CV_8UC4);
cv::VideoCapture cap;

void parse_args(int argc, char ** argv)
{
    std::vector<std::string> args;
    for (int i = 0; i < argc; i++)
        args.push_back(std::string(argv[i]));

    if (args[1]=="help")
    {
        synopsis(); exit(0);
    }

    if (args[1]=="epiphan")
    {
        // TODO
        return;
    }

    if (args[1]=="file")
    {
        test_mode = TestMode::File;

        if (argc < 4)
        {
            synopsis(); exit(-1);
        }

        cap.open(args[2]);
        if (not cap.isOpened())
        {
            std::cerr << "File " << args[2]
                      << " could not be opened"
                      << std::endl;
            exit(-1);
        }

        codec_string = args[3];
        num_frames = cap.get(CV_CAP_PROP_FRAME_COUNT);
        fps = cap.get(CV_CAP_PROP_FPS);
        duration = (num_frames / fps) / 60;
    }

    if (args[1]=="chess")
    {
        test_mode = TestMode::Chessboard;

        if (argc < 5)
        {
            synopsis(); exit(-1);
        }

        codec_string = args[2];
        width = atoi(argv[3]); height = atoi(argv[4]);
    }

    // health checks
    if (codec_string == "xvid")
        codec = gg::Target::File_XviD;
    else if (codec_string == "h265")
    {
        codec = gg::Target::File_H265;
        filetype = "mp4";
    }
    else
    {
        std::cerr << "Codec " << codec_string << " not recognised" << std::endl;
        synopsis();
        exit(-1);
    }
}

std::string which_file()
{
    std::string filename;
    switch(test_mode)
    {case TestMode::Epiphan:
        // TODO
        break;
    case TestMode::File:
        filename.append(std::to_string(duration))
                .append("min_")
                .append(codec_string)
                .append("_from_file.")
                .append(filetype);
        return filename;
    case TestMode::Chessboard:
        filename.append("1min_")
                .append(codec_string)
                .append("_colour_chessboard.")
                .append(filetype);
        return filename;
    default:
        std::cerr << "Test mode not set" << std::endl;
        exit(-1);
    }
}

void get_frame(VideoFrame_BGRA & frame)
{
    // Chessboard params
    float elapsed, remaining;
    int red, green, blue, alpha, min_intensity;
    switch(test_mode)
    {
    case TestMode::Chessboard:
        elapsed = (((float) i) / num_frames);
        remaining = 1 - elapsed;
        red = 0, green = 0, blue = 0, alpha = 0;
        min_intensity = 50; // don't want black
        for (int row = 0; row < height; row++)
        {
            green = min_intensity + elapsed * (((float) row) / height) * (255 - min_intensity);
            for (int col = 0; col < width; col++)
            {
                red = min_intensity + elapsed * (((float) col) / width) * (255 - min_intensity);
                blue = min_intensity +
                       remaining *
                            sqrt( ((float)(row*row + col*col))
                                  /
                                  (height*height+width*width) ) * (255 - min_intensity);
                cv::Mat square(square_size, square_size, CV_8UC4, cv::Scalar(blue, green, red, alpha));
                square.copyTo(image(cv::Rect(col * square_size , row * square_size,
                                             square.cols, square.rows)));
            }
        }
        frame.init_from_opencv_mat(image);
        break;
    case TestMode::Epiphan:
        // TODO
        break;
    case TestMode::File:
        cap >> image;
        cv::cvtColor(image, image, CV_BGR2BGRA);
        frame.init_from_opencv_mat(image);
        break;
    default:
        std::cerr << "Test mode not set" << std::endl;
        exit(-1);
    }
}

auto start = std::chrono::steady_clock::now();
float elapsed;
void time_left()
{
    if (i % 5 == 0)
        std::cout << "Saving frame " << i+1 << " of " << num_frames;

    int left;

    switch(test_mode)
    {
    case TestMode::Chessboard:
        // nop
        break;
    case TestMode::File:
    case TestMode::Epiphan:
        elapsed = std::chrono::duration_cast<std::chrono::seconds>(
                    std::chrono::steady_clock::now() - start).count();
        left = ((float) num_frames - i + 1) * elapsed / (i+1);
        std::cout << " (" << left << " sec. left)";
        break;
    default:
        std::cerr << "Test mode not set" << std::endl;
        exit(-1);
    }

    std::cout << "\r";
}

int main(int argc, char ** argv)
{
    parse_args(argc, argv);

    try
    {
        gg::IVideoTarget * file = gg::Factory::create(codec);
        std::string filename = which_file();
        file->init(filename, fps);
        std::cout << "Saving to file " << filename << std::endl;
        start = std::chrono::steady_clock::now();
        for (i = 0; i < num_frames; i++)
        {
            time_left();
            get_frame(frame);
            file->append(frame);
        }
        file->finalise();
    }
    catch (gg::VideoTargetError & e)
    {
        std::cerr << e.what() << std::endl;
        return -1;
    }

    return 0;
}
<|endoftext|>


<commit_before>#include <blackhole/formatter/json.hpp>
#include <blackhole/formatter/string.hpp>
#include <blackhole/repository/config/formatter.hpp>
#include <blackhole/repository/config/sink.hpp>
#include <blackhole/repository/factory/traits.hpp>
#include <blackhole/sink/files.hpp>

#include "global.hpp"

using namespace blackhole;

TEST(FactoryTraits, StringFormatterConfig) {
    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    auto actual = aux::config_mapper<formatter::string_t>::map(formatter.config);

    EXPECT_EQ("[%(timestamp)s]: %(message)s", actual.pattern);
}

TEST(FactoryTraits, JsonFormatterConfig) {
    formatter_config_t formatter("json");
    formatter["newline"] = true;
    formatter["mapping"]["message"] = "@message";
    formatter["routing"]["/"] = std::vector<std::string> { "message" };
    formatter["routing"]["/fields"] = "*";

    formatter::json::config_t actual = aux::config_mapper<formatter::json_t>::map(formatter.config);

    using namespace formatter::json::map;
    EXPECT_TRUE(actual.newline);
    ASSERT_TRUE(actual.naming.find("message") != actual.naming.end());
    EXPECT_EQ("@message", actual.naming["message"]);

    typedef std::unordered_map<std::string, routing_t::routes_t> specified_positioning_t;
    ASSERT_TRUE(actual.routing.specified.find("message") != actual.routing.specified.end());
    EXPECT_EQ(std::vector<std::string>({}), actual.routing.specified["message"]);
    EXPECT_EQ(std::vector<std::string>({ "fields" }), actual.routing.unspecified);
}

TEST(FactoryTraits, FileSinkConfig) {
    sink_config_t sink("files");
    sink["path"] = "/dev/null";
    sink["autoflush"] = false;

    auto actual = aux::config_mapper<sink::files_t<>>::map(sink.config);

    EXPECT_EQ("/dev/null", actual.path);
    EXPECT_FALSE(actual.autoflush);
}
<commit_msg>[Code Style] Removed unused typedef.<commit_after>#include <blackhole/formatter/json.hpp>
#include <blackhole/formatter/string.hpp>
#include <blackhole/repository/config/formatter.hpp>
#include <blackhole/repository/config/sink.hpp>
#include <blackhole/repository/factory/traits.hpp>
#include <blackhole/sink/files.hpp>

#include "global.hpp"

using namespace blackhole;

TEST(FactoryTraits, StringFormatterConfig) {
    formatter_config_t formatter("string");
    formatter["pattern"] = "[%(timestamp)s]: %(message)s";

    auto actual = aux::config_mapper<formatter::string_t>::map(formatter.config);

    EXPECT_EQ("[%(timestamp)s]: %(message)s", actual.pattern);
}

TEST(FactoryTraits, JsonFormatterConfig) {
    formatter_config_t formatter("json");
    formatter["newline"] = true;
    formatter["mapping"]["message"] = "@message";
    formatter["routing"]["/"] = std::vector<std::string> { "message" };
    formatter["routing"]["/fields"] = "*";

    formatter::json::config_t actual = aux::config_mapper<formatter::json_t>::map(formatter.config);

    using namespace formatter::json::map;
    EXPECT_TRUE(actual.newline);
    ASSERT_TRUE(actual.naming.find("message") != actual.naming.end());
    EXPECT_EQ("@message", actual.naming["message"]);

    ASSERT_TRUE(actual.routing.specified.find("message") != actual.routing.specified.end());
    EXPECT_EQ(std::vector<std::string>({}), actual.routing.specified["message"]);
    EXPECT_EQ(std::vector<std::string>({ "fields" }), actual.routing.unspecified);
}

TEST(FactoryTraits, FileSinkConfig) {
    sink_config_t sink("files");
    sink["path"] = "/dev/null";
    sink["autoflush"] = false;

    auto actual = aux::config_mapper<sink::files_t<>>::map(sink.config);

    EXPECT_EQ("/dev/null", actual.path);
    EXPECT_FALSE(actual.autoflush);
}
<|endoftext|>


<commit_before>// Copyright 2015 Vladimir Alyamkin. All Rights Reserved.
// Original code by https://github.com/unktomi

#include "VaRestEditorPluginPrivatePCH.h"
#include "VaRest_BreakJson.h"

#define LOCTEXT_NAMESPACE "VaRest_BreakJson"

class FKCHandler_BreakJson : public FNodeHandlingFunctor
{

public:
	FKCHandler_BreakJson(FKismetCompilerContext& InCompilerContext)
		: FNodeHandlingFunctor(InCompilerContext)
	{
	}

	virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
	{
		UEdGraphPin* InputPin = NULL;

		for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
		{
			UEdGraphPin* Pin = Node->Pins[PinIndex];
			if (Pin && (EGPD_Input == Pin->Direction))
			{
				InputPin = Pin;
				break;
			}
		}

		UEdGraphPin *InNet = FEdGraphUtilities::GetNetFromPin(InputPin);
		UClass *Class = Cast<UClass>(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'VaRestPlugin.VaRestJsonObject'")));

		FBPTerminal **SourceTerm = Context.NetMap.Find(InNet);
		if (SourceTerm == nullptr)
		{
			return;
		}

		for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
		{
			UEdGraphPin* Pin = Node->Pins[PinIndex];
			if (Pin && (EGPD_Output == Pin->Direction))
			{
				if (Pin->LinkedTo.Num() < 1)
				{
					continue;
				}

				FBPTerminal **Target = Context.NetMap.Find(Pin);

				const FString &FieldName = Pin->PinName;
				const FString &FieldType = Pin->PinType.PinCategory;

				FBPTerminal* FieldNameTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
				FieldNameTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_String;
				FieldNameTerm->Source = Pin;
				FieldNameTerm->Name = FieldName;
				FieldNameTerm->TextLiteral = FText::FromString(FieldName);

 				FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node);
				FName FunctionName;

				bool bIsArray = Pin->PinType.bIsArray;

				if (FieldType == CompilerContext.GetSchema()->PC_Boolean)
				{
					FunctionName = bIsArray ? TEXT("GetBoolArrayField") : TEXT("GetBoolField");
				}
				else if (FieldType == CompilerContext.GetSchema()->PC_Float)
				{
					FunctionName = bIsArray ? TEXT("GetNumberArrayField") : TEXT("GetNumberField");
				}
				else if (FieldType == CompilerContext.GetSchema()->PC_String)
				{
					FunctionName = bIsArray ? TEXT("GetStringArrayField") : TEXT("GetStringField");
				}
				else if (FieldType == CompilerContext.GetSchema()->PC_Object)
				{
					FunctionName = bIsArray ? TEXT("GetObjectArrayField") : TEXT("GetObjectField");
				}
				else
				{
					continue;
				}

				UFunction *FunctionPtr = Class->FindFunctionByName(FunctionName);
				Statement.Type = KCST_CallFunction;
				Statement.FunctionToCall = FunctionPtr;
				Statement.FunctionContext = *SourceTerm;
				Statement.bIsParentContext = false;
				Statement.LHS = *Target;
				Statement.RHS.Add(FieldNameTerm);
			}
		}
	}

	FBPTerminal* RegisterInputTerm(FKismetFunctionContext& Context, UVaRest_BreakJson* Node)
	{
		// Find input pin
		UEdGraphPin* InputPin = NULL;
		for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
		{
			UEdGraphPin* Pin = Node->Pins[PinIndex];
			if (Pin && (EGPD_Input == Pin->Direction))
			{
				InputPin = Pin;
				break;
			}
		}
		check(NULL != InputPin);

		// Find structure source net
		UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(InputPin);
		FBPTerminal **TermPtr = Context.NetMap.Find(Net);

		if (!TermPtr)
		{
			FBPTerminal *Term = Context.CreateLocalTerminalFromPinAutoChooseScope(Net, Context.NetNameMap->MakeValidName(Net));

			Context.NetMap.Add(Net, Term);

			return Term;
		}

		return *TermPtr;
	}

	void RegisterOutputTerm(FKismetFunctionContext& Context, UEdGraphPin* OutputPin, FBPTerminal* ContextTerm)
	{
		FBPTerminal *Term = Context.CreateLocalTerminalFromPinAutoChooseScope(OutputPin, Context.NetNameMap->MakeValidName(OutputPin));
		Context.NetMap.Add(OutputPin, Term);
	}

	virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* InNode) override
	{
		UVaRest_BreakJson* Node = Cast<UVaRest_BreakJson>(InNode);
		FNodeHandlingFunctor::RegisterNets(Context, Node);

		check(NULL != Node);

		if (FBPTerminal* StructContextTerm = RegisterInputTerm(Context, Node))
		{
			for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
			{
				UEdGraphPin* Pin = Node->Pins[PinIndex];
				if (NULL != Pin && EGPD_Output == Pin->Direction)
				{
					RegisterOutputTerm(Context, Pin, StructContextTerm);
				}
			}
		}
	}
};

/**
 * Main node class
 */
UVaRest_BreakJson::UVaRest_BreakJson(const FObjectInitializer &ObjectInitializer)
	: Super(ObjectInitializer)
{
}

FNodeHandlingFunctor* UVaRest_BreakJson::CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const
{
	return new FKCHandler_BreakJson(CompilerContext);
}

void UVaRest_BreakJson::AllocateDefaultPins()
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

	UClass *Class = Cast<UClass>(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'VaRestPlugin.VaRestJsonObject'")));
	UEdGraphPin* Pin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), Class, false, false, TEXT("Target"));

	K2Schema->SetPinDefaultValueBasedOnType(Pin);

	CreateProjectionPins(Pin);
}

FLinearColor UVaRest_BreakJson::GetNodeTitleColor() const
{
	return FLinearColor(255.0f, 255.0f, 0.0f);
}

void UVaRest_BreakJson::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
	bool bIsDirty = false;

	FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
	if (true || PropertyName == TEXT("Outputs"))
	{
		bIsDirty = true;
	}

	if (bIsDirty)
	{
		ReconstructNode();
		GetGraph()->NotifyGraphChanged();
	}

	Super::PostEditChangeProperty(PropertyChangedEvent);
}

void UVaRest_BreakJson::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
	// actions get registered under specific object-keys; the idea is that
	// actions might have to be updated (or deleted) if their object-key is
	// mutated (or removed)... here we use the node's class (so if the node
	// type disappears, then the action should go with it)
	UClass* ActionKey = GetClass();

	// to keep from needlessly instantiating a UBlueprintNodeSpawner, first
	// check to make sure that the registrar is looking for actions of this type
	// (could be regenerating actions for a specific asset, and therefore the
	// registrar would only accept actions corresponding to that asset)
	if (ActionRegistrar.IsOpenForRegistration(ActionKey))
	{
		UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
		check(NodeSpawner != nullptr);

		ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
	}
}

FText UVaRest_BreakJson::GetMenuCategory() const
{
	static FNodeTextCache CachedCategory;

	if (CachedCategory.IsOutOfDate(this))
	{
		// FText::Format() is slow, so we cache this to save on performance
		CachedCategory.SetCachedText(FEditorCategoryUtils::BuildCategoryString(FCommonEditorCategory::Utilities, LOCTEXT("ActionMenuCategory", "Va Rest")), this);
	}
	return CachedCategory;
}

void UVaRest_BreakJson::CreateProjectionPins(UEdGraphPin *Source)
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
	UClass *Class = Cast<UClass>(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'VaRestPlugin.VaRestJsonObject'")));

	for (TArray<FVaRest_NamedType>::TIterator it(Outputs); it; ++it)
	{
		FString Type;
		UObject *Subtype = nullptr;
		FString FieldName = (*it).Name;

		switch ((*it).Type) {
			case EVaRest_JsonType::JSON_Bool:
				Type = K2Schema->PC_Boolean;
				break;

			case EVaRest_JsonType::JSON_Number:
				Type = K2Schema->PC_Float;
				break;

			case EVaRest_JsonType::JSON_String:
				Type = K2Schema->PC_String;
				break;

			case EVaRest_JsonType::JSON_Object:
				Type = K2Schema->PC_Object;
				Subtype = Class;
				break;
		}

		UEdGraphPin *OutputPin = CreatePin(EGPD_Output, Type, TEXT(""), Subtype, (*it).bIsArray, false, (*it).Name);
	}
}

FText UVaRest_BreakJson::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
	return LOCTEXT("VaRest_Break_Json.NodeTitle", "Break Json");
}
<commit_msg>Fix compilation issues on Mac for UE 4.11<commit_after>// Copyright 2015 Vladimir Alyamkin. All Rights Reserved.
// Original code by https://github.com/unktomi

#include "VaRestEditorPluginPrivatePCH.h"
#include "VaRest_BreakJson.h"

#define LOCTEXT_NAMESPACE "VaRest_BreakJson"

class FKCHandler_BreakJson : public FNodeHandlingFunctor
{

public:
	FKCHandler_BreakJson(FKismetCompilerContext& InCompilerContext)
		: FNodeHandlingFunctor(InCompilerContext)
	{
	}

	virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
	{
		UEdGraphPin* InputPin = NULL;

		for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
		{
			UEdGraphPin* Pin = Node->Pins[PinIndex];
			if (Pin && (EGPD_Input == Pin->Direction))
			{
				InputPin = Pin;
				break;
			}
		}

		UEdGraphPin *InNet = FEdGraphUtilities::GetNetFromPin(InputPin);
		UClass *Class = Cast<UClass>(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'VaRestPlugin.VaRestJsonObject'")));

		FBPTerminal **SourceTerm = Context.NetMap.Find(InNet);
		if (SourceTerm == nullptr)
		{
			return;
		}

		for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
		{
			UEdGraphPin* Pin = Node->Pins[PinIndex];
			if (Pin && (EGPD_Output == Pin->Direction))
			{
				if (Pin->LinkedTo.Num() < 1)
				{
					continue;
				}

				FBPTerminal **Target = Context.NetMap.Find(Pin);

				const FString &FieldName = Pin->PinName;
				const FString &FieldType = Pin->PinType.PinCategory;

				FBPTerminal* FieldNameTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal);
				FieldNameTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_String;
				FieldNameTerm->Source = Pin;
				FieldNameTerm->Name = FieldName;
				FieldNameTerm->TextLiteral = FText::FromString(FieldName);

 				FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node);
				FName FunctionName;

				bool bIsArray = Pin->PinType.bIsArray;

				if (FieldType == CompilerContext.GetSchema()->PC_Boolean)
				{
					FunctionName = bIsArray ? TEXT("GetBoolArrayField") : TEXT("GetBoolField");
				}
				else if (FieldType == CompilerContext.GetSchema()->PC_Float)
				{
					FunctionName = bIsArray ? TEXT("GetNumberArrayField") : TEXT("GetNumberField");
				}
				else if (FieldType == CompilerContext.GetSchema()->PC_String)
				{
					FunctionName = bIsArray ? TEXT("GetStringArrayField") : TEXT("GetStringField");
				}
				else if (FieldType == CompilerContext.GetSchema()->PC_Object)
				{
					FunctionName = bIsArray ? TEXT("GetObjectArrayField") : TEXT("GetObjectField");
				}
				else
				{
					continue;
				}

				UFunction *FunctionPtr = Class->FindFunctionByName(FunctionName);
				Statement.Type = KCST_CallFunction;
				Statement.FunctionToCall = FunctionPtr;
				Statement.FunctionContext = *SourceTerm;
				Statement.bIsParentContext = false;
				Statement.LHS = *Target;
				Statement.RHS.Add(FieldNameTerm);
			}
		}
	}

	FBPTerminal* RegisterInputTerm(FKismetFunctionContext& Context, UVaRest_BreakJson* Node)
	{
		// Find input pin
		UEdGraphPin* InputPin = NULL;
		for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
		{
			UEdGraphPin* Pin = Node->Pins[PinIndex];
			if (Pin && (EGPD_Input == Pin->Direction))
			{
				InputPin = Pin;
				break;
			}
		}
		check(NULL != InputPin);

		// Find structure source net
		UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(InputPin);
		FBPTerminal **TermPtr = Context.NetMap.Find(Net);

		if (!TermPtr)
		{
			FBPTerminal *Term = Context.CreateLocalTerminalFromPinAutoChooseScope(Net, Context.NetNameMap->MakeValidName(Net));

			Context.NetMap.Add(Net, Term);

			return Term;
		}

		return *TermPtr;
	}

	void RegisterOutputTerm(FKismetFunctionContext& Context, UEdGraphPin* OutputPin, FBPTerminal* ContextTerm)
	{
		FBPTerminal *Term = Context.CreateLocalTerminalFromPinAutoChooseScope(OutputPin, Context.NetNameMap->MakeValidName(OutputPin));
		Context.NetMap.Add(OutputPin, Term);
	}

	virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* InNode) override
	{
		UVaRest_BreakJson* Node = Cast<UVaRest_BreakJson>(InNode);
		FNodeHandlingFunctor::RegisterNets(Context, Node);

		check(NULL != Node);

		if (FBPTerminal* StructContextTerm = RegisterInputTerm(Context, Node))
		{
			for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex)
			{
				UEdGraphPin* Pin = Node->Pins[PinIndex];
				if (NULL != Pin && EGPD_Output == Pin->Direction)
				{
					RegisterOutputTerm(Context, Pin, StructContextTerm);
				}
			}
		}
	}
};

/**
 * Main node class
 */
UVaRest_BreakJson::UVaRest_BreakJson(const FObjectInitializer &ObjectInitializer)
	: Super(ObjectInitializer)
{
}

FNodeHandlingFunctor* UVaRest_BreakJson::CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const
{
	return new FKCHandler_BreakJson(CompilerContext);
}

void UVaRest_BreakJson::AllocateDefaultPins()
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

	UClass *Class = Cast<UClass>(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'VaRestPlugin.VaRestJsonObject'")));
	UEdGraphPin* Pin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), Class, false, false, TEXT("Target"));

	K2Schema->SetPinDefaultValueBasedOnType(Pin);

	CreateProjectionPins(Pin);
}

FLinearColor UVaRest_BreakJson::GetNodeTitleColor() const
{
	return FLinearColor(255.0f, 255.0f, 0.0f);
}

void UVaRest_BreakJson::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
	bool bIsDirty = false;

	FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
	if (true || PropertyName == TEXT("Outputs"))
	{
		bIsDirty = true;
	}

	if (bIsDirty)
	{
		ReconstructNode();
		GetGraph()->NotifyGraphChanged();
	}

	Super::PostEditChangeProperty(PropertyChangedEvent);
}

void UVaRest_BreakJson::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
	// actions get registered under specific object-keys; the idea is that
	// actions might have to be updated (or deleted) if their object-key is
	// mutated (or removed)... here we use the node's class (so if the node
	// type disappears, then the action should go with it)
	UClass* ActionKey = GetClass();

	// to keep from needlessly instantiating a UBlueprintNodeSpawner, first
	// check to make sure that the registrar is looking for actions of this type
	// (could be regenerating actions for a specific asset, and therefore the
	// registrar would only accept actions corresponding to that asset)
	if (ActionRegistrar.IsOpenForRegistration(ActionKey))
	{
		UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
		check(NodeSpawner != nullptr);

		ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
	}
}

FText UVaRest_BreakJson::GetMenuCategory() const
{
	static FNodeTextCache CachedCategory;

	if (CachedCategory.IsOutOfDate(this))
	{
		// FText::Format() is slow, so we cache this to save on performance
		CachedCategory.SetCachedText(FEditorCategoryUtils::BuildCategoryString(FCommonEditorCategory::Utilities, LOCTEXT("ActionMenuCategory", "Va Rest")), this);
	}
	return CachedCategory;
}

void UVaRest_BreakJson::CreateProjectionPins(UEdGraphPin *Source)
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
	UClass *Class = Cast<UClass>(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'VaRestPlugin.VaRestJsonObject'")));

	for (TArray<FVaRest_NamedType>::TIterator it(Outputs); it; ++it)
	{
		FString Type;
		UObject *Subtype = nullptr;
		FString FieldName = (*it).Name;

		switch ((*it).Type) {
			case EVaRest_JsonType::JSON_Bool:
				Type = K2Schema->PC_Boolean;
				break;

			case EVaRest_JsonType::JSON_Number:
				Type = K2Schema->PC_Float;
				break;

			case EVaRest_JsonType::JSON_String:
				Type = K2Schema->PC_String;
				break;

			case EVaRest_JsonType::JSON_Object:
				Type = K2Schema->PC_Object;
				Subtype = Class;
				break;
		}

		UEdGraphPin *OutputPin = CreatePin(EGPD_Output, Type, TEXT(""), Subtype, (*it).bIsArray, false, (*it).Name);
	}
}

FText UVaRest_BreakJson::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
	return LOCTEXT("VaRest_Break_Json.NodeTitle", "Break Json");
}

#undef LOCTEXT_NAMESPACE
<|endoftext|>