<commit_before>/*
 * main.cpp - Kurento Media Server
 *
 * Copyright (C) 2013 Kurento
 * Contact: Miguel París Díaz <mparisdiaz@gmail.com>
 * Contact: José Antonio Santos Cadenas <santoscadenas@kurento.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3
 * as published by the Free Software Foundation.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <signal.h>
#include <execinfo.h>

#include "MediaServerServiceHandler.hpp"

#include <protocol/TBinaryProtocol.h>
#include <transport/TServerSocket.h>
#include <transport/TBufferTransports.h>
#include <server/TNonblockingServer.h>
#include <concurrency/PosixThreadFactory.h>
#include <concurrency/ThreadManager.h>
#include <KmsHttpEPServer.h>

#include "media_config.hpp"

#include <glibmm.h>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
#include <version.hpp>
#include "log.hpp"

#define GST_DEFAULT_NAME "media_server"

GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);

using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;
using namespace ::apache::thrift::concurrency;

using namespace ::kurento;

using boost::shared_ptr;
using namespace boost::filesystem;
using ::kurento::MediaServerServiceHandler;
using ::Glib::KeyFile;
using ::Glib::KeyFileFlags;

static std::string serverAddress, httpEPServerAddress;
static gint serverServicePort, httpEPServerServicePort;
GstSDPMessage *sdpPattern;

Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create (true);
static TNonblockingServer *p_server = NULL;

static gchar *conf_file;

static GOptionEntry entries[] = {
  {
    "conf-file", 'f', 0, G_OPTION_ARG_FILENAME, &conf_file, "Configuration file",
    NULL
  },
  {NULL}
};

static void
create_media_server_service ()
{
  shared_ptr < MediaServerServiceHandler >
  handler (new MediaServerServiceHandler () );
  shared_ptr < TProcessor >
  processor (new MediaServerServiceProcessor (handler) );
  shared_ptr < TProtocolFactory >
  protocolFactory (new TBinaryProtocolFactory () );
  shared_ptr < PosixThreadFactory > threadFactory (new PosixThreadFactory () );
  shared_ptr < ThreadManager > threadManager =
    ThreadManager::newSimpleThreadManager (15);
  threadManager->threadFactory (threadFactory);
  threadManager->start ();
  TNonblockingServer server (processor, protocolFactory, serverServicePort, threadManager);
  p_server = &server;
  GST_INFO ("Starting MediaServerService");
  kill (getppid(), SIGCONT);
  server.serve ();
  GST_INFO ("MediaServerService stopped finishing thread");
  throw Glib::Thread::Exit ();
}

static void
check_port (int port)
{
  if (port <= 0 || port > G_MAXUSHORT)
    throw Glib::KeyFileError (Glib::KeyFileError::PARSE, "Invalid value");
}

static void
set_default_media_server_config ()
{
  GST_WARNING ("Setting default configuration for media server. "
      "Using IP address: %s, port: %d. "
      "No codecs support will be available with default configuration.",
      MEDIA_SERVER_ADDRESS, MEDIA_SERVER_SERVICE_PORT);

  serverAddress = MEDIA_SERVER_ADDRESS;
  serverServicePort = MEDIA_SERVER_SERVICE_PORT;
}

static void
set_default_http_ep_server_config ()
{
  GST_WARNING ("Setting default configuration for http end point server. "
      "Using IP address: %s, port: %d. ",
      HTTP_EP_SERVER_ADDRESS, HTTP_EP_SERVER_SERVICE_PORT);

  httpEPServerAddress = HTTP_EP_SERVER_ADDRESS;
  httpEPServerServicePort = HTTP_EP_SERVER_SERVICE_PORT;
}

static void
set_default_config ()
{
  set_default_media_server_config ();
  set_default_http_ep_server_config();
}

static gchar *
read_entire_file (const gchar *file_name)
{
  gchar *data;
  long f_size;
  FILE *fp;

  fp = fopen (file_name, "r");

  if (fp == NULL) {
    return NULL;
  }

  fseek (fp, 0, SEEK_END);
  f_size = ftell (fp);
  fseek (fp, 0, SEEK_SET);
  data = (gchar *) g_malloc0 (f_size);
  fread (data, 1, f_size, fp);
  fclose (fp);

  return data;
}

static GstSDPMessage *
load_sdp_pattern (Glib::KeyFile &configFile, const std::string &confFileName)
{
  GstSDPResult result;
  GstSDPMessage *sdp_pattern = NULL;
  gchar *sdp_pattern_text;
  std::string sdp_pattern_file_name;

  GST_DEBUG ("Load SDP Pattern");
  result = gst_sdp_message_new (&sdp_pattern);

  if (result != GST_SDP_OK) {
    GST_ERROR ("Error creating sdp message");
    return NULL;
  }

  sdp_pattern_file_name = configFile.get_string (SERVER_GROUP, SDP_PATTERN_KEY);
  boost::filesystem::path p (confFileName.c_str () );
  sdp_pattern_file_name.insert (0, "/");
  sdp_pattern_file_name.insert (0, p.parent_path ().c_str() );
  sdp_pattern_text = read_entire_file (sdp_pattern_file_name.c_str () );

  if (sdp_pattern_text == NULL) {
    GST_ERROR ("Error reading SDP pattern file");
    gst_sdp_message_free (sdp_pattern);
    return NULL;
  }

  result = gst_sdp_message_parse_buffer ( (const guint8 *) sdp_pattern_text, -1, sdp_pattern);
  g_free (sdp_pattern_text);

  if (result != GST_SDP_OK) {
    GST_ERROR ("Error parsing SDP config pattern");
    gst_sdp_message_free (sdp_pattern);
    return NULL;
  }

  return sdp_pattern;
}

static void
configure_kurento_media_server (KeyFile &configFile, const std::string &file_name)
{
  gint port;
  gchar *sdpMessageText = NULL;

  try {
    serverAddress = configFile.get_string (SERVER_GROUP,
        MEDIA_SERVER_ADDRESS_KEY);
  } catch (Glib::KeyFileError err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default address %s to media server",
        MEDIA_SERVER_ADDRESS);
    serverAddress = MEDIA_SERVER_ADDRESS;
  }

  try {
    port = configFile.get_integer (SERVER_GROUP, MEDIA_SERVER_SERVICE_PORT_KEY);
    check_port (port);
    serverServicePort = port;
  } catch (Glib::KeyFileError err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default port %d to media server",
        MEDIA_SERVER_SERVICE_PORT);
    serverServicePort = MEDIA_SERVER_SERVICE_PORT;
  }

  try {
    sdpPattern = load_sdp_pattern (configFile, file_name);
    GST_DEBUG ("SDP: \n%s", sdpMessageText = gst_sdp_message_as_text (sdpPattern) );
    g_free (sdpMessageText);
  } catch (Glib::KeyFileError err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Wrong codec configuration, communication won't be possible");
  }
}

static void
configure_http_ep_server (KeyFile &configFile)
{
  gint port;

  try {
    httpEPServerAddress = configFile.get_string (HTTP_EP_SERVER_GROUP,
        HTTP_EP_SERVER_ADDRESS_KEY);
  } catch (Glib::KeyFileError err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default address %s to http end point server",
        HTTP_EP_SERVER_ADDRESS);
    httpEPServerAddress = HTTP_EP_SERVER_ADDRESS;
  }

  try {
    port = configFile.get_integer (HTTP_EP_SERVER_GROUP, HTTP_EP_SERVER_SERVICE_PORT_KEY);
    check_port (port);
    httpEPServerServicePort = port;
  } catch (Glib::KeyFileError err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default port %d to http end point server",
        HTTP_EP_SERVER_SERVICE_PORT);
    httpEPServerServicePort = HTTP_EP_SERVER_SERVICE_PORT;
  }
}

static void
load_config (const std::string &file_name)
{
  KeyFile configFile;

  GST_INFO ("Reading configuration from: %s", file_name.c_str () );

  /* Try to load configuration file */
  try {
    if (!configFile.load_from_file (file_name,
        KeyFileFlags::KEY_FILE_KEEP_COMMENTS |
        KeyFileFlags::KEY_FILE_KEEP_TRANSLATIONS) ) {
      GST_WARNING ("Can not load configuration file %s", file_name.c_str () );
      set_default_config ();
      return;
    }
  } catch (Glib::Error ex) {
    GST_ERROR ("Error loading configuration: %s", ex.what ().c_str () );
    set_default_config ();
    return;
  }

  /* parse options so as to configure servers */
  configure_kurento_media_server (configFile, file_name);
  configure_http_ep_server (configFile);

  GST_INFO ("Configuration loaded successfully");
}

static void
initialiseExecutableName (char *exe, int size)
{
  char link[1024];
  int len;
  snprintf (link, sizeof (link), "/proc/%d/exe", getpid () );
  len = readlink (link, exe, size);

  if (len == -1) {
    fprintf (stderr, "ERROR GETTING NAME\n");
    exit (1);
  }

  exe[len] = '\0';
}

static const char *
getExecutableName ()
{
  static char *exe = NULL;
  static char aux[1024];

  if (exe == NULL) {
    initialiseExecutableName (aux, sizeof (aux) );
    exe = aux;
  }

  return exe;
}

static bool
quit_loop ()
{
  loop->quit ();

  return FALSE;
}

static void
bt_sighandler (int sig, siginfo_t *info, gpointer data)
{
  void *trace[35];
  char **messages = (char **) NULL;
  int i, trace_size = 0;

//      ucontext_t *uc = (ucontext_t *)data;

  /* Do something useful with siginfo_t */
  if (sig == SIGSEGV) {
    printf ("Got signal %d, faulty address is %p\n", sig,
        (gpointer) info->si_addr);
  } else if (sig == SIGKILL || sig == SIGINT) {
    /* since we connect to a signal handler, asynchronous management might */
    /* might happen so we need to set an idle handler to exit the main loop */
    /* in the mainloop context. */
    Glib::RefPtr<Glib::IdleSource> idle_source = Glib::IdleSource::create ();
    idle_source->connect (sigc::ptr_fun (&quit_loop) );
    idle_source->attach (loop->get_context() );
    return;
  } else {
    printf ("Got signal %d\n", sig);
  }

  trace_size = backtrace (trace, 35);
  /* overwrite sigaction with caller's address */
  //trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];
  messages = backtrace_symbols (trace, trace_size);
  /* skip first stack frame (points here) */
  g_print ("\t[bt] Execution path:\n");

  for (i = 1; i < trace_size; ++i) {
    g_print ("\t[bt] #%d %s\n", i, messages[i]);
    char syscom[256];
    gchar **strs;
    const gchar *exe;
    strs = g_strsplit (messages[i], "(", 2);

    if (strs[1] == NULL)
      exe = getExecutableName ();
    else
      exe = strs[0];

    sprintf (syscom, "echo -n \"\t[bt]\t\t\"; addr2line %p -s -e %s",
        trace[i], exe);
    g_strfreev (strs);
    system (syscom);
  }

  if (sig == SIGPIPE) {
    GST_DEBUG ("Ignore sigpipe");
  } else {
    exit (sig);
  }
}

static void
http_server_start_cb (KmsHttpEPServer *self, GError *err)
{
  GST_DEBUG ("HttpEPServer started");
}

int
main (int argc, char **argv)
{
  KmsHttpEPServer *httpepserver;
  GError *error = NULL;
  GOptionContext *context;
  struct sigaction sa;

  context = g_option_context_new ("");
  g_option_context_add_main_entries (context, entries, NULL);
  g_option_context_add_group (context, gst_init_get_option_group () );

  if (!g_option_context_parse (context, &argc, &argv, &error) ) {
    GST_ERROR ("option parsing failed: %s\n", error->message);
    exit (1);
  }

  g_option_context_free (context);

  gst_init (&argc, &argv);
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
      GST_DEFAULT_NAME);

  /* Install our signal handler */
  sa.sa_sigaction = /*(void (*)(int, siginfo*, gpointer)) */ bt_sighandler;
  sigemptyset (&sa.sa_mask);
  sa.sa_flags = SA_RESTART | SA_SIGINFO;
  sigaction (SIGSEGV, &sa, NULL);
  sigaction (SIGPIPE, &sa, NULL);
  sigaction (SIGINT, &sa, NULL);
  sigaction (SIGKILL, &sa, NULL);
  Glib::thread_init ();
  GST_INFO ("Kmsc version: %s", get_version () );

  if (!conf_file)
    load_config (DEFAULT_CONFIG_FILE);
  else
    load_config ( (std::string) conf_file);

  sigc::slot < void >ss = sigc::ptr_fun (&create_media_server_service);
  Glib::Thread::create (ss, true); /* Created thread not used to join
                                      because of a bug in thrift */

  /* Start Http End Point Server */
  httpepserver = kms_http_ep_server_new (
      KMS_HTTP_EP_SERVER_PORT, httpEPServerServicePort,
      KMS_HTTP_EP_SERVER_INTERFACE, httpEPServerAddress.c_str(), NULL);
  kms_http_ep_server_start (httpepserver, http_server_start_cb);

  loop->run ();

  /* Stop Http End Point Server and destroy it */
  kms_http_ep_server_stop (httpepserver);
  g_object_unref (G_OBJECT (httpepserver) );

  return 0;
}
<commit_msg>Delay thrift starting up until http server is ready to attend requests<commit_after>/*
 * main.cpp - Kurento Media Server
 *
 * Copyright (C) 2013 Kurento
 * Contact: Miguel París Díaz <mparisdiaz@gmail.com>
 * Contact: José Antonio Santos Cadenas <santoscadenas@kurento.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3
 * as published by the Free Software Foundation.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <signal.h>
#include <execinfo.h>

#include "MediaServerServiceHandler.hpp"

#include <protocol/TBinaryProtocol.h>
#include <transport/TServerSocket.h>
#include <transport/TBufferTransports.h>
#include <server/TNonblockingServer.h>
#include <concurrency/PosixThreadFactory.h>
#include <concurrency/ThreadManager.h>
#include <KmsHttpEPServer.h>

#include "media_config.hpp"

#include <glibmm.h>
#include <fstream>
#include <iostream>
#include <boost/filesystem.hpp>
#include <version.hpp>
#include "log.hpp"

#define GST_DEFAULT_NAME "media_server"

GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);

using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;
using namespace ::apache::thrift::concurrency;

using namespace ::kurento;

using boost::shared_ptr;
using namespace boost::filesystem;
using ::kurento::MediaServerServiceHandler;
using ::Glib::KeyFile;
using ::Glib::KeyFileFlags;

static std::string serverAddress, httpEPServerAddress;
static gint serverServicePort, httpEPServerServicePort;
GstSDPMessage *sdpPattern;

Glib::RefPtr<Glib::MainLoop> loop = Glib::MainLoop::create (true);
static TNonblockingServer *p_server = NULL;

static gchar *conf_file;

static GOptionEntry entries[] = {
  {
    "conf-file", 'f', 0, G_OPTION_ARG_FILENAME, &conf_file, "Configuration file",
    NULL
  },
  {NULL}
};

static void
create_media_server_service ()
{
  shared_ptr < MediaServerServiceHandler >
  handler (new MediaServerServiceHandler () );
  shared_ptr < TProcessor >
  processor (new MediaServerServiceProcessor (handler) );
  shared_ptr < TProtocolFactory >
  protocolFactory (new TBinaryProtocolFactory () );
  shared_ptr < PosixThreadFactory > threadFactory (new PosixThreadFactory () );
  shared_ptr < ThreadManager > threadManager =
    ThreadManager::newSimpleThreadManager (15);
  threadManager->threadFactory (threadFactory);
  threadManager->start ();
  TNonblockingServer server (processor, protocolFactory, serverServicePort, threadManager);
  p_server = &server;
  GST_INFO ("Starting MediaServerService");
  kill (getppid(), SIGCONT);
  server.serve ();
  GST_INFO ("MediaServerService stopped finishing thread");
  throw Glib::Thread::Exit ();
}

static void
check_port (int port)
{
  if (port <= 0 || port > G_MAXUSHORT)
    throw Glib::KeyFileError (Glib::KeyFileError::PARSE, "Invalid value");
}

static void
set_default_media_server_config ()
{
  GST_WARNING ("Setting default configuration for media server. "
      "Using IP address: %s, port: %d. "
      "No codecs support will be available with default configuration.",
      MEDIA_SERVER_ADDRESS, MEDIA_SERVER_SERVICE_PORT);

  serverAddress = MEDIA_SERVER_ADDRESS;
  serverServicePort = MEDIA_SERVER_SERVICE_PORT;
}

static void
set_default_http_ep_server_config ()
{
  GST_WARNING ("Setting default configuration for http end point server. "
      "Using IP address: %s, port: %d. ",
      HTTP_EP_SERVER_ADDRESS, HTTP_EP_SERVER_SERVICE_PORT);

  httpEPServerAddress = HTTP_EP_SERVER_ADDRESS;
  httpEPServerServicePort = HTTP_EP_SERVER_SERVICE_PORT;
}

static void
set_default_config ()
{
  set_default_media_server_config ();
  set_default_http_ep_server_config();
}

static gchar *
read_entire_file (const gchar *file_name)
{
  gchar *data;
  long f_size;
  FILE *fp;

  fp = fopen (file_name, "r");

  if (fp == NULL) {
    return NULL;
  }

  fseek (fp, 0, SEEK_END);
  f_size = ftell (fp);
  fseek (fp, 0, SEEK_SET);
  data = (gchar *) g_malloc0 (f_size);
  fread (data, 1, f_size, fp);
  fclose (fp);

  return data;
}

static GstSDPMessage *
load_sdp_pattern (Glib::KeyFile &configFile, const std::string &confFileName)
{
  GstSDPResult result;
  GstSDPMessage *sdp_pattern = NULL;
  gchar *sdp_pattern_text;
  std::string sdp_pattern_file_name;

  GST_DEBUG ("Load SDP Pattern");
  result = gst_sdp_message_new (&sdp_pattern);

  if (result != GST_SDP_OK) {
    GST_ERROR ("Error creating sdp message");
    return NULL;
  }

  sdp_pattern_file_name = configFile.get_string (SERVER_GROUP, SDP_PATTERN_KEY);
  boost::filesystem::path p (confFileName.c_str () );
  sdp_pattern_file_name.insert (0, "/");
  sdp_pattern_file_name.insert (0, p.parent_path ().c_str() );
  sdp_pattern_text = read_entire_file (sdp_pattern_file_name.c_str () );

  if (sdp_pattern_text == NULL) {
    GST_ERROR ("Error reading SDP pattern file");
    gst_sdp_message_free (sdp_pattern);
    return NULL;
  }

  result = gst_sdp_message_parse_buffer ( (const guint8 *) sdp_pattern_text, -1, sdp_pattern);
  g_free (sdp_pattern_text);

  if (result != GST_SDP_OK) {
    GST_ERROR ("Error parsing SDP config pattern");
    gst_sdp_message_free (sdp_pattern);
    return NULL;
  }

  return sdp_pattern;
}

static void
configure_kurento_media_server (KeyFile &configFile, const std::string &file_name)
{
  gint port;
  gchar *sdpMessageText = NULL;

  try {
    serverAddress = configFile.get_string (SERVER_GROUP,
        MEDIA_SERVER_ADDRESS_KEY);
  } catch (Glib::KeyFileError err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default address %s to media server",
        MEDIA_SERVER_ADDRESS);
    serverAddress = MEDIA_SERVER_ADDRESS;
  }

  try {
    port = configFile.get_integer (SERVER_GROUP, MEDIA_SERVER_SERVICE_PORT_KEY);
    check_port (port);
    serverServicePort = port;
  } catch (Glib::KeyFileError err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default port %d to media server",
        MEDIA_SERVER_SERVICE_PORT);
    serverServicePort = MEDIA_SERVER_SERVICE_PORT;
  }

  try {
    sdpPattern = load_sdp_pattern (configFile, file_name);
    GST_DEBUG ("SDP: \n%s", sdpMessageText = gst_sdp_message_as_text (sdpPattern) );
    g_free (sdpMessageText);
  } catch (Glib::KeyFileError err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Wrong codec configuration, communication won't be possible");
  }
}

static void
configure_http_ep_server (KeyFile &configFile)
{
  gint port;

  try {
    httpEPServerAddress = configFile.get_string (HTTP_EP_SERVER_GROUP,
        HTTP_EP_SERVER_ADDRESS_KEY);
  } catch (Glib::KeyFileError err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default address %s to http end point server",
        HTTP_EP_SERVER_ADDRESS);
    httpEPServerAddress = HTTP_EP_SERVER_ADDRESS;
  }

  try {
    port = configFile.get_integer (HTTP_EP_SERVER_GROUP, HTTP_EP_SERVER_SERVICE_PORT_KEY);
    check_port (port);
    httpEPServerServicePort = port;
  } catch (Glib::KeyFileError err) {
    GST_ERROR ("%s", err.what ().c_str () );
    GST_WARNING ("Setting default port %d to http end point server",
        HTTP_EP_SERVER_SERVICE_PORT);
    httpEPServerServicePort = HTTP_EP_SERVER_SERVICE_PORT;
  }
}

static void
load_config (const std::string &file_name)
{
  KeyFile configFile;

  GST_INFO ("Reading configuration from: %s", file_name.c_str () );

  /* Try to load configuration file */
  try {
    if (!configFile.load_from_file (file_name,
        KeyFileFlags::KEY_FILE_KEEP_COMMENTS |
        KeyFileFlags::KEY_FILE_KEEP_TRANSLATIONS) ) {
      GST_WARNING ("Can not load configuration file %s", file_name.c_str () );
      set_default_config ();
      return;
    }
  } catch (Glib::Error ex) {
    GST_ERROR ("Error loading configuration: %s", ex.what ().c_str () );
    set_default_config ();
    return;
  }

  /* parse options so as to configure servers */
  configure_kurento_media_server (configFile, file_name);
  configure_http_ep_server (configFile);

  GST_INFO ("Configuration loaded successfully");
}

static void
initialiseExecutableName (char *exe, int size)
{
  char link[1024];
  int len;
  snprintf (link, sizeof (link), "/proc/%d/exe", getpid () );
  len = readlink (link, exe, size);

  if (len == -1) {
    fprintf (stderr, "ERROR GETTING NAME\n");
    exit (1);
  }

  exe[len] = '\0';
}

static const char *
getExecutableName ()
{
  static char *exe = NULL;
  static char aux[1024];

  if (exe == NULL) {
    initialiseExecutableName (aux, sizeof (aux) );
    exe = aux;
  }

  return exe;
}

static bool
quit_loop ()
{
  loop->quit ();

  return FALSE;
}

static void
bt_sighandler (int sig, siginfo_t *info, gpointer data)
{
  void *trace[35];
  char **messages = (char **) NULL;
  int i, trace_size = 0;

//      ucontext_t *uc = (ucontext_t *)data;

  /* Do something useful with siginfo_t */
  if (sig == SIGSEGV) {
    printf ("Got signal %d, faulty address is %p\n", sig,
        (gpointer) info->si_addr);
  } else if (sig == SIGKILL || sig == SIGINT) {
    /* since we connect to a signal handler, asynchronous management might */
    /* might happen so we need to set an idle handler to exit the main loop */
    /* in the mainloop context. */
    Glib::RefPtr<Glib::IdleSource> idle_source = Glib::IdleSource::create ();
    idle_source->connect (sigc::ptr_fun (&quit_loop) );
    idle_source->attach (loop->get_context() );
    return;
  } else {
    printf ("Got signal %d\n", sig);
  }

  trace_size = backtrace (trace, 35);
  /* overwrite sigaction with caller's address */
  //trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];
  messages = backtrace_symbols (trace, trace_size);
  /* skip first stack frame (points here) */
  g_print ("\t[bt] Execution path:\n");

  for (i = 1; i < trace_size; ++i) {
    g_print ("\t[bt] #%d %s\n", i, messages[i]);
    char syscom[256];
    gchar **strs;
    const gchar *exe;
    strs = g_strsplit (messages[i], "(", 2);

    if (strs[1] == NULL)
      exe = getExecutableName ();
    else
      exe = strs[0];

    sprintf (syscom, "echo -n \"\t[bt]\t\t\"; addr2line %p -s -e %s",
        trace[i], exe);
    g_strfreev (strs);
    system (syscom);
  }

  if (sig == SIGPIPE) {
    GST_DEBUG ("Ignore sigpipe");
  } else {
    exit (sig);
  }
}

static void
http_server_start_cb (KmsHttpEPServer *self, GError *err)
{
  if (err != NULL) {
    GST_ERROR ("Http server could not start. Reason: %s", err->message);
    loop->quit ();
    return;
  }

  GST_DEBUG ("HttpEPServer started. Setting up thrift server service.");

  /* Created thread not used for joining because of a bug in thrift */
  sigc::slot < void >ss = sigc::ptr_fun (&create_media_server_service);
  Glib::Thread::create (ss, true);
}

int
main (int argc, char **argv)
{
  KmsHttpEPServer *httpepserver;
  GError *error = NULL;
  GOptionContext *context;
  struct sigaction sa;

  context = g_option_context_new ("");
  g_option_context_add_main_entries (context, entries, NULL);
  g_option_context_add_group (context, gst_init_get_option_group () );

  if (!g_option_context_parse (context, &argc, &argv, &error) ) {
    GST_ERROR ("option parsing failed: %s\n", error->message);
    exit (1);
  }

  g_option_context_free (context);

  gst_init (&argc, &argv);
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
      GST_DEFAULT_NAME);

  /* Install our signal handler */
  sa.sa_sigaction = /*(void (*)(int, siginfo*, gpointer)) */ bt_sighandler;
  sigemptyset (&sa.sa_mask);
  sa.sa_flags = SA_RESTART | SA_SIGINFO;
  sigaction (SIGSEGV, &sa, NULL);
  sigaction (SIGPIPE, &sa, NULL);
  sigaction (SIGINT, &sa, NULL);
  sigaction (SIGKILL, &sa, NULL);
  Glib::thread_init ();
  GST_INFO ("Kmsc version: %s", get_version () );

  if (!conf_file)
    load_config (DEFAULT_CONFIG_FILE);
  else
    load_config ( (std::string) conf_file);

  /* Start Http End Point Server */
  GST_DEBUG ("Starting Http ens point server.");
  httpepserver = kms_http_ep_server_new (
      KMS_HTTP_EP_SERVER_PORT, httpEPServerServicePort,
      KMS_HTTP_EP_SERVER_INTERFACE, httpEPServerAddress.c_str(), NULL);
  kms_http_ep_server_start (httpepserver, http_server_start_cb);

  loop->run ();

  /* Stop Http End Point Server and destroy it */
  kms_http_ep_server_stop (httpepserver);
  g_object_unref (G_OBJECT (httpepserver) );

  return 0;
}
<|endoftext|>


<commit_before>/*
 * NotebookExec.cpp
 *
 * Copyright (C) 2009-17 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionRmdNotebook.hpp"
#include "NotebookExec.hpp"
#include "NotebookOutput.hpp"
#include "NotebookPlots.hpp"
#include "NotebookHtmlWidgets.hpp"
#include "NotebookCache.hpp"
#include "NotebookData.hpp"
#include "NotebookErrors.hpp"
#include "NotebookWorkingDir.hpp"
#include "NotebookConditions.hpp"

#include <boost/foreach.hpp>

#include <core/Error.hpp>
#include <core/text/CsvParser.hpp>
#include <core/FileSerializer.hpp>

#include <r/ROptions.hpp>

#include <session/SessionModuleContext.hpp>
#include <session/SessionUserSettings.hpp>

#include <iostream>

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace rmarkdown {
namespace notebook {

namespace {

FilePath getNextOutputFile(const std::string& docId, const std::string& chunkId,
   const std::string& nbCtxId, ChunkOutputType outputType, unsigned *pOrdinal)
{
   OutputPair pair = lastChunkOutput(docId, chunkId, nbCtxId);
   pair.ordinal++;
   pair.outputType = outputType;
   if (pOrdinal)
      *pOrdinal = pair.ordinal;
   FilePath target = chunkOutputFile(docId, chunkId, nbCtxId, pair);
   updateLastChunkOutput(docId, chunkId, pair);
   return target;
}

} // anonymous namespace

core::Error copyLibDirForOutput(const core::FilePath& file,
   const std::string& docId, const std::string& nbCtxId)
{
   Error error = Success();

   FilePath fileLib = file.parent().complete(kChunkLibDir);
   if (fileLib.exists())
   {
      std::string docPath;
      source_database::getPath(docId, &docPath);
      error = mergeLib(fileLib, chunkCacheFolder(docPath, docId, nbCtxId)
                                   .complete(kChunkLibDir));
      if (error)
         LOG_ERROR(error);

      error = fileLib.remove();
      if (error)
         LOG_ERROR(error);
   }

   return error;
}

ChunkExecContext::ChunkExecContext(const std::string& docId,
      const std::string& chunkId, const std::string& nbCtxId,
      ExecScope execScope, const core::FilePath& workingDir,
      const ChunkOptions& options, int pixelWidth, int charWidth):
   docId_(docId),
   chunkId_(chunkId),
   nbCtxId_(nbCtxId),
   workingDir_(workingDir),
   options_(options),
   pixelWidth_(pixelWidth),
   charWidth_(charWidth),
   prevCharWidth_(0),
   lastOutputType_(kChunkConsoleInput),
   execScope_(execScope),
   hasOutput_(false),
   hasErrors_(false)
{
}

std::string ChunkExecContext::chunkId()
{
   return chunkId_;
}

std::string ChunkExecContext::docId()
{
   return docId_;
}

const ChunkOptions& ChunkExecContext::options()
{
   return options_;
}

void ChunkExecContext::connect()
{
   outputPath_ = chunkOutputPath(docId_, chunkId_ + kStagingSuffix, nbCtxId_,
         ContextExact);
   Error error = outputPath_.ensureDirectory();
   if (error)
   {
      // if we don't have a place to put the output, don't register any handlers
      // (will end in tears)
      LOG_ERROR(error);
      return;
   }

   // if executing the whole chunk, initialize output right away (otherwise we
   // wait until we actually have output)
   if (execScope_ == ExecScopeChunk)
      initializeOutput();

   // capture conditions
   boost::shared_ptr<ConditionCapture> pConditionCapture =
      boost::make_shared<ConditionCapture>();
   pConditionCapture->connect();
   captures_.push_back(pConditionCapture);
   connections_.push_back(events().onCondition.connect(
         boost::bind(&ChunkExecContext::onCondition, this, _1, _2)));

   // extract knitr figure options if present
   double figWidth = options_.getOverlayOption("fig.width", 0.0);
   double figHeight = options_.getOverlayOption("fig.height", 0.0);

   // if 'fig.asp' is set, then use that to override 'fig.height'
   double figAsp = options_.getOverlayOption("fig.asp", 0.0);
   if (figAsp != 0.0)
   {
      // if figWidth is unset, default to 7.0
      if (figWidth == 0.0)
         figWidth = 7.0;

      figHeight = figWidth * figAsp;
   }

   // begin capturing plots
   connections_.push_back(events().onPlotOutput.connect(
         boost::bind(&ChunkExecContext::onFileOutput, this, _1, _2,
                     _3, ChunkOutputPlot, _4)));

   boost::shared_ptr<PlotCapture> pPlotCapture =
      boost::make_shared<PlotCapture>();
   captures_.push_back(pPlotCapture);

   if (figWidth > 0 || figHeight > 0)
   {
      // user specified plot size, use it
      error = pPlotCapture->connectPlots(docId_, chunkId_, nbCtxId_,
            figHeight, figWidth, PlotSizeManual, outputPath_);
   }
   else
   {
      // user didn't specify plot size, use the width of the editor surface
      error = pPlotCapture->connectPlots(docId_, chunkId_, nbCtxId_,
            0, pixelWidth_, PlotSizeAutomatic, outputPath_);
   }
   if (error)
      LOG_ERROR(error);

   // begin capturing HTML input
   connections_.push_back(events().onHtmlOutput.connect(
         boost::bind(&ChunkExecContext::onFileOutput, this, _1, _2, _3,
                     ChunkOutputHtml, 0)));

   boost::shared_ptr<HtmlCapture> pHtmlCapture =
      boost::make_shared<HtmlCapture>();
   captures_.push_back(pHtmlCapture);

   error = pHtmlCapture->connectHtmlCapture(
            outputPath_,
            outputPath_.parent().complete(kChunkLibDir),
            options_.chunkOptions());
   if (error)
      LOG_ERROR(error);

   // log warnings immediately (unless user's changed the default warning
   // level)
   r::sexp::Protect protect;
   SEXP warnSEXP;
   error = r::exec::RFunction("getOption", "warn").call(&warnSEXP, &protect);
   if (!error)
   {
      prevWarn_.set(warnSEXP);

      // default warning setting is 1 (log immediately), but if the warning
      // option is set to FALSE, we want to set it to -1 (ignore warnings)
      bool warning = options_.getOverlayOption("warning", true);
      error = r::options::setOption<int>("warn", warning ? 1 : -1);
      if (error)
         LOG_ERROR(error);
   }

   // reset width
   prevCharWidth_ = r::options::getOptionWidth();
   r::options::setOptionWidth(charWidth_);

   boost::shared_ptr<DirCapture> pDirCapture = boost::make_shared<DirCapture>();
   error = pDirCapture->connectDir(docId_, workingDir_);
   if (error)
      LOG_ERROR(error);
   else
      captures_.push_back(pDirCapture);

   // begin capturing errors
   boost::shared_ptr<ErrorCapture> pErrorCapture =
      boost::make_shared<ErrorCapture>();
   pErrorCapture->connect();
   captures_.push_back(pErrorCapture);

   connections_.push_back(events().onErrorOutput.connect(
         boost::bind(&ChunkExecContext::onError, this, _1)));

   // begin capturing console text
   connections_.push_back(module_context::events().onConsoleOutput.connect(
         boost::bind(&ChunkExecContext::onConsoleOutput, this, _1, _2)));
   connections_.push_back(module_context::events().onConsoleInput.connect(
         boost::bind(&ChunkExecContext::onConsoleInput, this, _1)));

   // begin capturing data
   connections_.push_back(events().onDataOutput.connect(
         boost::bind(&ChunkExecContext::onFileOutput, this, _1, _2, _3,
                     ChunkOutputData, 0)));

   boost::shared_ptr<DataCapture> pDataCapture =
      boost::make_shared<DataCapture>();
   captures_.push_back(pDataCapture);

   error = pDataCapture->connectDataCapture(
            outputPath_,
            options_.mergedOptions());
   if (error)
      LOG_ERROR(error);

   NotebookCapture::connect();
}

bool ChunkExecContext::onCondition(Condition condition,
      const std::string& message)
{
   // skip if the user has asked us to suppress this kind of condition
   if (condition == ConditionMessage &&
       !options_.getOverlayOption("message", true))
   {
      return false;
   }
   if (condition == ConditionWarning &&
       !options_.getOverlayOption("warning", true))
   {
      return false;
   }

   // give each capturing module a chance to handle the condition
   BOOST_FOREACH(boost::shared_ptr<NotebookCapture> pCapture, captures_)
   {
      if (pCapture->onCondition(condition, message))
         return true;
   }

   // none of them did; treat it as ordinary output
   onConsoleOutput(module_context::ConsoleOutputError, message);
   module_context::enqueClientEvent(
      ClientEvent(client_events::kConsoleWriteError, message));

   return true;
}

void ChunkExecContext::onFileOutput(const FilePath& file,
      const FilePath& sidecar, const core::json::Value& metadata,
      ChunkOutputType outputType, unsigned ordinal)
{
   // set up folder to receive output if necessary
   initializeOutput();

   // put the file in sequence inside the host directory
   FilePath target;
   if (ordinal == 0)
   {
      // unspecified ordinal, generate one
      target = getNextOutputFile(docId_, chunkId_, nbCtxId_, outputType,
            &ordinal);
   }
   else
   {
      // known ordinal, use it (this can occur for out of sequence events, such
      // as plots)
      OutputPair pair(outputType, ordinal);
      target = chunkOutputFile(docId_, chunkId_, nbCtxId_, pair);
   }

   // preserve original extension; some output types, such as plots, don't
   // have a canonical extension
   target = target.parent().complete(target.stem() + file.extension());

   Error error = file.move(target);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // check to see if the file has an accompanying library folder; if so, move
   // it to the global library folder
   copyLibDirForOutput(file, docId_, nbCtxId_);

   // if output sidecar file was provided, write it out
   if (!sidecar.empty())
   {
      sidecar.move(target.parent().complete(
               target.stem() + sidecar.extension()));
   }

   // serialize metadata if provided
   if (!metadata.is_null())
   {
      std::ostringstream oss;
      json::write(metadata, oss);
      error = writeStringToFile(target.parent().complete(
               target.stem() + ".metadata"), oss.str());
   }

   enqueueChunkOutput(docId_, chunkId_, nbCtxId_, ordinal, outputType, target,
         metadata);
}

void ChunkExecContext::onError(const core::json::Object& err)
{
   // set up folder to receive output if necessary
   initializeOutput();

   // mark error state
   hasErrors_ = true;

   // write the error to a file
   unsigned ordinal;
   FilePath target = getNextOutputFile(docId_, chunkId_, nbCtxId_,
         ChunkOutputError, &ordinal);
   boost::shared_ptr<std::ostream> pOfs;
   Error error = target.open_w(&pOfs, true);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }
   json::write(err, *pOfs);

   pOfs->flush();
   pOfs.reset();

   // send to client
   enqueueChunkOutput(docId_, chunkId_, nbCtxId_, ordinal, ChunkOutputError,
                      target);
}

void ChunkExecContext::onConsoleText(int type, const std::string& output,
      bool truncate)
{
   // if we haven't received any actual output yet, don't push input into the
   // file yet
   if (type == kChunkConsoleInput && !hasOutput_)
   {
      pendingInput_.append(output + "\n");
      return;
   }

   // blank lines aren't permitted following output, only input
   if (lastOutputType_ != kChunkConsoleInput && type == kChunkConsoleInput && output.empty())
      return;
   lastOutputType_ = type;

   // set up folder to receive output if necessary
   initializeOutput();

   // flush any buffered pending input
   if (!pendingInput_.empty())
   {
      std::string input = pendingInput_;
      pendingInput_.clear();
      onConsoleText(kChunkConsoleInput, input, true);
   }

   // determine output filename and ensure it exists
   FilePath outputCsv = chunkOutputFile(docId_, chunkId_, nbCtxId_,
         ChunkOutputText);
   Error error = outputCsv.ensureFile();
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   std::vector<std::string> vals;
   vals.push_back(safe_convert::numberToString(type));
   vals.push_back(output);
   error = core::writeStringToFile(outputCsv,
         text::encodeCsvLine(vals) + "\n",
         string_utils::LineEndingPassthrough, truncate);
   if (error)
   {
      LOG_ERROR(error);
   }

   // if we got some real output, fire event for it
   if (!output.empty())
      events().onChunkConsoleOutput(docId_, chunkId_, type, output);
}

void ChunkExecContext::disconnect()
{
   Error error;

   // clean up capturing modules (includes plots, errors, and HTML widgets)
   BOOST_FOREACH(boost::shared_ptr<NotebookCapture> pCapture, captures_)
   {
      pCapture->disconnect();
   }

   // clean up staging folder
   error = outputPath_.removeIfExists();
   if (error)
      LOG_ERROR(error);

   // restore width value
   r::options::setOptionWidth(prevCharWidth_);

   // restore preserved warning level, if any
   if (!prevWarn_.isNil())
   {
      error = r::options::setOption("warn", prevWarn_.get());
      if (error)
         LOG_ERROR(error);
   }

   // unhook all our event handlers
   BOOST_FOREACH(const RSTUDIO_BOOST_CONNECTION& connection, connections_)
   {
      connection.disconnect();
   }

   NotebookCapture::disconnect();

   events().onChunkExecCompleted(docId_, chunkId_, nbCtxId_);
}

void ChunkExecContext::onConsoleOutput(module_context::ConsoleOutputType type,
      const std::string& output)
{
   if (type == module_context::ConsoleOutputNormal)
      onConsoleText(kChunkConsoleOutput, output, false);
   else
      onConsoleText(kChunkConsoleError, output, false);
}

void ChunkExecContext::onConsoleInput(const std::string& input)
{
   onConsoleText(kChunkConsoleInput, input, false);
}

void ChunkExecContext::initializeOutput()
{
   // if we already have output, do nothing
   if (hasOutput_)
      return;

   // if we don't have output yet, clean existing output before adding new
   // output
   Error error = cleanChunkOutput(docId_, chunkId_, nbCtxId_, true);
   if (error)
      LOG_ERROR(error);

   // ensure that the output folder exists
   error = chunkOutputPath(docId_, chunkId_, nbCtxId_, ContextExact)
      .ensureDirectory();
   if (error)
      LOG_ERROR(error);

   hasOutput_ = true;
}

bool ChunkExecContext::hasErrors()
{
   return hasErrors_;
}

ExecScope ChunkExecContext::execScope()
{
   return execScope_;
}

void ChunkExecContext::onExprComplete()
{
   // notify capturing submodules
   BOOST_FOREACH(boost::shared_ptr<NotebookCapture> pCapture, captures_)
   {
      pCapture->onExprComplete();
   }
}

} // namespace notebook
} // namespace rmarkdown
} // namespace modules
} // namespace session
} // namespace rstudio

<commit_msg>set 'rstudio.notebook.executing' during chunk execution<commit_after>/*
 * NotebookExec.cpp
 *
 * Copyright (C) 2009-17 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionRmdNotebook.hpp"
#include "NotebookExec.hpp"
#include "NotebookOutput.hpp"
#include "NotebookPlots.hpp"
#include "NotebookHtmlWidgets.hpp"
#include "NotebookCache.hpp"
#include "NotebookData.hpp"
#include "NotebookErrors.hpp"
#include "NotebookWorkingDir.hpp"
#include "NotebookConditions.hpp"

#include <boost/foreach.hpp>

#include <core/Error.hpp>
#include <core/text/CsvParser.hpp>
#include <core/FileSerializer.hpp>

#include <r/ROptions.hpp>

#include <session/SessionModuleContext.hpp>
#include <session/SessionUserSettings.hpp>

#include <iostream>

using namespace rstudio::core;

#define kRStudioNotebookExecuting ("rstudio.notebook.executing")

namespace rstudio {
namespace session {
namespace modules {
namespace rmarkdown {
namespace notebook {

namespace {

FilePath getNextOutputFile(const std::string& docId, const std::string& chunkId,
   const std::string& nbCtxId, ChunkOutputType outputType, unsigned *pOrdinal)
{
   OutputPair pair = lastChunkOutput(docId, chunkId, nbCtxId);
   pair.ordinal++;
   pair.outputType = outputType;
   if (pOrdinal)
      *pOrdinal = pair.ordinal;
   FilePath target = chunkOutputFile(docId, chunkId, nbCtxId, pair);
   updateLastChunkOutput(docId, chunkId, pair);
   return target;
}

} // anonymous namespace

core::Error copyLibDirForOutput(const core::FilePath& file,
   const std::string& docId, const std::string& nbCtxId)
{
   Error error = Success();

   FilePath fileLib = file.parent().complete(kChunkLibDir);
   if (fileLib.exists())
   {
      std::string docPath;
      source_database::getPath(docId, &docPath);
      error = mergeLib(fileLib, chunkCacheFolder(docPath, docId, nbCtxId)
                                   .complete(kChunkLibDir));
      if (error)
         LOG_ERROR(error);

      error = fileLib.remove();
      if (error)
         LOG_ERROR(error);
   }

   return error;
}

ChunkExecContext::ChunkExecContext(const std::string& docId,
      const std::string& chunkId, const std::string& nbCtxId,
      ExecScope execScope, const core::FilePath& workingDir,
      const ChunkOptions& options, int pixelWidth, int charWidth):
   docId_(docId),
   chunkId_(chunkId),
   nbCtxId_(nbCtxId),
   workingDir_(workingDir),
   options_(options),
   pixelWidth_(pixelWidth),
   charWidth_(charWidth),
   prevCharWidth_(0),
   lastOutputType_(kChunkConsoleInput),
   execScope_(execScope),
   hasOutput_(false),
   hasErrors_(false)
{
}

std::string ChunkExecContext::chunkId()
{
   return chunkId_;
}

std::string ChunkExecContext::docId()
{
   return docId_;
}

const ChunkOptions& ChunkExecContext::options()
{
   return options_;
}

void ChunkExecContext::connect()
{
   outputPath_ = chunkOutputPath(docId_, chunkId_ + kStagingSuffix, nbCtxId_,
         ContextExact);
   Error error = outputPath_.ensureDirectory();
   if (error)
   {
      // if we don't have a place to put the output, don't register any handlers
      // (will end in tears)
      LOG_ERROR(error);
      return;
   }

   // if executing the whole chunk, initialize output right away (otherwise we
   // wait until we actually have output)
   if (execScope_ == ExecScopeChunk)
      initializeOutput();

   // capture conditions
   boost::shared_ptr<ConditionCapture> pConditionCapture =
      boost::make_shared<ConditionCapture>();
   pConditionCapture->connect();
   captures_.push_back(pConditionCapture);
   connections_.push_back(events().onCondition.connect(
         boost::bind(&ChunkExecContext::onCondition, this, _1, _2)));

   // extract knitr figure options if present
   double figWidth = options_.getOverlayOption("fig.width", 0.0);
   double figHeight = options_.getOverlayOption("fig.height", 0.0);

   // if 'fig.asp' is set, then use that to override 'fig.height'
   double figAsp = options_.getOverlayOption("fig.asp", 0.0);
   if (figAsp != 0.0)
   {
      // if figWidth is unset, default to 7.0
      if (figWidth == 0.0)
         figWidth = 7.0;

      figHeight = figWidth * figAsp;
   }

   // begin capturing plots
   connections_.push_back(events().onPlotOutput.connect(
         boost::bind(&ChunkExecContext::onFileOutput, this, _1, _2,
                     _3, ChunkOutputPlot, _4)));

   boost::shared_ptr<PlotCapture> pPlotCapture =
      boost::make_shared<PlotCapture>();
   captures_.push_back(pPlotCapture);

   if (figWidth > 0 || figHeight > 0)
   {
      // user specified plot size, use it
      error = pPlotCapture->connectPlots(docId_, chunkId_, nbCtxId_,
            figHeight, figWidth, PlotSizeManual, outputPath_);
   }
   else
   {
      // user didn't specify plot size, use the width of the editor surface
      error = pPlotCapture->connectPlots(docId_, chunkId_, nbCtxId_,
            0, pixelWidth_, PlotSizeAutomatic, outputPath_);
   }
   if (error)
      LOG_ERROR(error);

   // begin capturing HTML input
   connections_.push_back(events().onHtmlOutput.connect(
         boost::bind(&ChunkExecContext::onFileOutput, this, _1, _2, _3,
                     ChunkOutputHtml, 0)));

   boost::shared_ptr<HtmlCapture> pHtmlCapture =
      boost::make_shared<HtmlCapture>();
   captures_.push_back(pHtmlCapture);

   error = pHtmlCapture->connectHtmlCapture(
            outputPath_,
            outputPath_.parent().complete(kChunkLibDir),
            options_.chunkOptions());
   if (error)
      LOG_ERROR(error);

   // log warnings immediately (unless user's changed the default warning
   // level)
   r::sexp::Protect protect;
   SEXP warnSEXP;
   error = r::exec::RFunction("getOption", "warn").call(&warnSEXP, &protect);
   if (!error)
   {
      prevWarn_.set(warnSEXP);

      // default warning setting is 1 (log immediately), but if the warning
      // option is set to FALSE, we want to set it to -1 (ignore warnings)
      bool warning = options_.getOverlayOption("warning", true);
      error = r::options::setOption<int>("warn", warning ? 1 : -1);
      if (error)
         LOG_ERROR(error);
   }

   // broadcast that we're executing in a Notebook
   r::options::setOption(kRStudioNotebookExecuting, true);

   // reset width
   prevCharWidth_ = r::options::getOptionWidth();
   r::options::setOptionWidth(charWidth_);

   boost::shared_ptr<DirCapture> pDirCapture = boost::make_shared<DirCapture>();
   error = pDirCapture->connectDir(docId_, workingDir_);
   if (error)
      LOG_ERROR(error);
   else
      captures_.push_back(pDirCapture);

   // begin capturing errors
   boost::shared_ptr<ErrorCapture> pErrorCapture =
      boost::make_shared<ErrorCapture>();
   pErrorCapture->connect();
   captures_.push_back(pErrorCapture);

   connections_.push_back(events().onErrorOutput.connect(
         boost::bind(&ChunkExecContext::onError, this, _1)));

   // begin capturing console text
   connections_.push_back(module_context::events().onConsoleOutput.connect(
         boost::bind(&ChunkExecContext::onConsoleOutput, this, _1, _2)));
   connections_.push_back(module_context::events().onConsoleInput.connect(
         boost::bind(&ChunkExecContext::onConsoleInput, this, _1)));

   // begin capturing data
   connections_.push_back(events().onDataOutput.connect(
         boost::bind(&ChunkExecContext::onFileOutput, this, _1, _2, _3,
                     ChunkOutputData, 0)));

   boost::shared_ptr<DataCapture> pDataCapture =
      boost::make_shared<DataCapture>();
   captures_.push_back(pDataCapture);

   error = pDataCapture->connectDataCapture(
            outputPath_,
            options_.mergedOptions());
   if (error)
      LOG_ERROR(error);

   NotebookCapture::connect();
}

bool ChunkExecContext::onCondition(Condition condition,
      const std::string& message)
{
   // skip if the user has asked us to suppress this kind of condition
   if (condition == ConditionMessage &&
       !options_.getOverlayOption("message", true))
   {
      return false;
   }
   if (condition == ConditionWarning &&
       !options_.getOverlayOption("warning", true))
   {
      return false;
   }

   // give each capturing module a chance to handle the condition
   BOOST_FOREACH(boost::shared_ptr<NotebookCapture> pCapture, captures_)
   {
      if (pCapture->onCondition(condition, message))
         return true;
   }

   // none of them did; treat it as ordinary output
   onConsoleOutput(module_context::ConsoleOutputError, message);
   module_context::enqueClientEvent(
      ClientEvent(client_events::kConsoleWriteError, message));

   return true;
}

void ChunkExecContext::onFileOutput(const FilePath& file,
      const FilePath& sidecar, const core::json::Value& metadata,
      ChunkOutputType outputType, unsigned ordinal)
{
   // set up folder to receive output if necessary
   initializeOutput();

   // put the file in sequence inside the host directory
   FilePath target;
   if (ordinal == 0)
   {
      // unspecified ordinal, generate one
      target = getNextOutputFile(docId_, chunkId_, nbCtxId_, outputType,
            &ordinal);
   }
   else
   {
      // known ordinal, use it (this can occur for out of sequence events, such
      // as plots)
      OutputPair pair(outputType, ordinal);
      target = chunkOutputFile(docId_, chunkId_, nbCtxId_, pair);
   }

   // preserve original extension; some output types, such as plots, don't
   // have a canonical extension
   target = target.parent().complete(target.stem() + file.extension());

   Error error = file.move(target);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   // check to see if the file has an accompanying library folder; if so, move
   // it to the global library folder
   copyLibDirForOutput(file, docId_, nbCtxId_);

   // if output sidecar file was provided, write it out
   if (!sidecar.empty())
   {
      sidecar.move(target.parent().complete(
               target.stem() + sidecar.extension()));
   }

   // serialize metadata if provided
   if (!metadata.is_null())
   {
      std::ostringstream oss;
      json::write(metadata, oss);
      error = writeStringToFile(target.parent().complete(
               target.stem() + ".metadata"), oss.str());
   }

   enqueueChunkOutput(docId_, chunkId_, nbCtxId_, ordinal, outputType, target,
         metadata);
}

void ChunkExecContext::onError(const core::json::Object& err)
{
   // set up folder to receive output if necessary
   initializeOutput();

   // mark error state
   hasErrors_ = true;

   // write the error to a file
   unsigned ordinal;
   FilePath target = getNextOutputFile(docId_, chunkId_, nbCtxId_,
         ChunkOutputError, &ordinal);
   boost::shared_ptr<std::ostream> pOfs;
   Error error = target.open_w(&pOfs, true);
   if (error)
   {
      LOG_ERROR(error);
      return;
   }
   json::write(err, *pOfs);

   pOfs->flush();
   pOfs.reset();

   // send to client
   enqueueChunkOutput(docId_, chunkId_, nbCtxId_, ordinal, ChunkOutputError,
                      target);
}

void ChunkExecContext::onConsoleText(int type, const std::string& output,
      bool truncate)
{
   // if we haven't received any actual output yet, don't push input into the
   // file yet
   if (type == kChunkConsoleInput && !hasOutput_)
   {
      pendingInput_.append(output + "\n");
      return;
   }

   // blank lines aren't permitted following output, only input
   if (lastOutputType_ != kChunkConsoleInput && type == kChunkConsoleInput && output.empty())
      return;
   lastOutputType_ = type;

   // set up folder to receive output if necessary
   initializeOutput();

   // flush any buffered pending input
   if (!pendingInput_.empty())
   {
      std::string input = pendingInput_;
      pendingInput_.clear();
      onConsoleText(kChunkConsoleInput, input, true);
   }

   // determine output filename and ensure it exists
   FilePath outputCsv = chunkOutputFile(docId_, chunkId_, nbCtxId_,
         ChunkOutputText);
   Error error = outputCsv.ensureFile();
   if (error)
   {
      LOG_ERROR(error);
      return;
   }

   std::vector<std::string> vals;
   vals.push_back(safe_convert::numberToString(type));
   vals.push_back(output);
   error = core::writeStringToFile(outputCsv,
         text::encodeCsvLine(vals) + "\n",
         string_utils::LineEndingPassthrough, truncate);
   if (error)
   {
      LOG_ERROR(error);
   }

   // if we got some real output, fire event for it
   if (!output.empty())
      events().onChunkConsoleOutput(docId_, chunkId_, type, output);
}

void ChunkExecContext::disconnect()
{
   Error error;

   // clean up capturing modules (includes plots, errors, and HTML widgets)
   BOOST_FOREACH(boost::shared_ptr<NotebookCapture> pCapture, captures_)
   {
      pCapture->disconnect();
   }

   // clean up staging folder
   error = outputPath_.removeIfExists();
   if (error)
      LOG_ERROR(error);

   // broadcast that we're done with notebook execution
   r::options::setOption(kRStudioNotebookExecuting, false);

   // restore width value
   r::options::setOptionWidth(prevCharWidth_);

   // restore preserved warning level, if any
   if (!prevWarn_.isNil())
   {
      error = r::options::setOption("warn", prevWarn_.get());
      if (error)
         LOG_ERROR(error);
   }

   // unhook all our event handlers
   BOOST_FOREACH(const RSTUDIO_BOOST_CONNECTION& connection, connections_)
   {
      connection.disconnect();
   }

   NotebookCapture::disconnect();

   events().onChunkExecCompleted(docId_, chunkId_, nbCtxId_);
}

void ChunkExecContext::onConsoleOutput(module_context::ConsoleOutputType type,
      const std::string& output)
{
   if (type == module_context::ConsoleOutputNormal)
      onConsoleText(kChunkConsoleOutput, output, false);
   else
      onConsoleText(kChunkConsoleError, output, false);
}

void ChunkExecContext::onConsoleInput(const std::string& input)
{
   onConsoleText(kChunkConsoleInput, input, false);
}

void ChunkExecContext::initializeOutput()
{
   // if we already have output, do nothing
   if (hasOutput_)
      return;

   // if we don't have output yet, clean existing output before adding new
   // output
   Error error = cleanChunkOutput(docId_, chunkId_, nbCtxId_, true);
   if (error)
      LOG_ERROR(error);

   // ensure that the output folder exists
   error = chunkOutputPath(docId_, chunkId_, nbCtxId_, ContextExact)
      .ensureDirectory();
   if (error)
      LOG_ERROR(error);

   hasOutput_ = true;
}

bool ChunkExecContext::hasErrors()
{
   return hasErrors_;
}

ExecScope ChunkExecContext::execScope()
{
   return execScope_;
}

void ChunkExecContext::onExprComplete()
{
   // notify capturing submodules
   BOOST_FOREACH(boost::shared_ptr<NotebookCapture> pCapture, captures_)
   {
      pCapture->onExprComplete();
   }
}

} // namespace notebook
} // namespace rmarkdown
} // namespace modules
} // namespace session
} // namespace rstudio

<|endoftext|>


<commit_before>
#pragma once

#include "geometry/affine_map.hpp"
#include "geometry/frame.hpp"
#include "geometry/orthogonal_map.hpp"
#include "geometry/named_quantities.hpp"
#include "geometry/perspective.hpp"
#include "geometry/rotation.hpp"
#include "geometry/rp2_point.hpp"
#include "geometry/sphere.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/elementary_functions.hpp"
#include "quantities/quantities.hpp"
#include "quantities/si.hpp"
#include "testing_utilities/almost_equals.hpp"
#include "testing_utilities/componentwise.hpp"
#include "testing_utilities/vanishes_before.hpp"

namespace principia {
namespace geometry {
namespace internal_perspective {

using quantities::Length;
using quantities::Sqrt;
using quantities::si::Metre;
using quantities::si::Radian;
using testing_utilities::AlmostEquals;
using testing_utilities::Componentwise;
using testing_utilities::VanishesBefore;
using ::testing::Eq;

class PerspectiveTest : public ::testing::Test {
 protected:
  using World = Frame<serialization::Frame::TestTag,
                      serialization::Frame::TEST1, false>;
  using Camera = Frame<serialization::Frame::TestTag,
                       serialization::Frame::TEST2, false>;
};

TEST_F(PerspectiveTest, Basic) {
  Point<Displacement<World>> const camera_origin =
      World::origin + Displacement<World>({1 * Metre, 2 * Metre, -3 * Metre});
  Rotation<World, Camera> const world_to_camera_rotation(
      π / 6 * Radian,
      π / 4 * Radian,
      π / 3 * Radian,
      CardanoAngles::ZYX,
      DefinesFrame<Camera>());
  AffineMap<World, Camera, Length, OrthogonalMap> const world_to_camera_affine(
      camera_origin,
      Camera::origin,
      world_to_camera_rotation.Forget());
  Perspective<World, Camera, Length, OrthogonalMap> perspective(
      world_to_camera_affine,
      /*focal=*/10 * Metre);

  // Check that points in the camera z axis get projected to the origin of ℝP².
  Displacement<World> const camera_z_axis = world_to_camera_rotation.Inverse()(
      Displacement<Camera>({0 * Metre, 0 * Metre, 1 * Metre}));
  Point<Displacement<World>> const p0 = camera_origin;
  Point<Displacement<World>> const p1 = camera_origin + 1 * camera_z_axis;
  Point<Displacement<World>> const p2 = camera_origin + 10 * camera_z_axis;
  EXPECT_TRUE(perspective(p0)->is_at_infinity());
  EXPECT_THAT(*perspective(p1),
              Componentwise(VanishesBefore(1 * Metre, 10),
                            VanishesBefore(1 * Metre, 0)));
  EXPECT_THAT(*perspective(p2),
              Componentwise(VanishesBefore(1 * Metre, 8),
                            VanishesBefore(1 * Metre, 4)));

  // Check that points on the camera x axis get projected on the x axis of ℝP².
  Displacement<World> const camera_x_axis = world_to_camera_rotation.Inverse()(
      Displacement<Camera>({1 * Metre, 0 * Metre, 0 * Metre}));
  Point<Displacement<World>> const p3 = p1 + 5 * camera_x_axis;
  Point<Displacement<World>> const p4 = p1 + 7 * camera_x_axis;
  EXPECT_THAT(perspective(p3)->y(), VanishesBefore(1 * Metre, 20));
  EXPECT_THAT(perspective(p4)->y(), VanishesBefore(1 * Metre, 10));

  // Check that points on the camera y axis get projected on the y axis of ℝP².
  Displacement<World> const camera_y_axis = world_to_camera_rotation.Inverse()(
      Displacement<Camera>({0 * Metre, 1 * Metre, 0 * Metre}));
  Point<Displacement<World>> const p5 = p1 - 11 * camera_y_axis;
  Point<Displacement<World>> const p6 = p1 + 13 * camera_y_axis;
  EXPECT_THAT(perspective(p5)->x(), VanishesBefore(1 * Metre, 120));
  EXPECT_THAT(perspective(p6)->x(), VanishesBefore(1 * Metre, 0));

  // Check that aligned points are aligned in ℝP².
  Point<Displacement<World>> const p7 =
      camera_origin +
      Displacement<World>({17 * Metre, -23 * Metre, 29 * Metre});
  Point<Displacement<World>> const p8 =
      camera_origin +
      Displacement<World>({18 * Metre, -21 * Metre, 24 * Metre});
  Point<Displacement<World>> const p9 =
      camera_origin +
      Displacement<World>({19 * Metre, -19 * Metre, 19 * Metre});
  auto const q7 = perspective(p7);
  auto const q8 = perspective(p8);
  auto const q9 = perspective(p9);
  EXPECT_THAT((q8->x() - q7->x()) * (q9->y() - q7->y()) -
                  (q9->x() - q7->x()) * (q8->y() - q7->y()),
              VanishesBefore(1 * Metre * Metre, 6));

  // Check that the focal works as expected.
  Point<Displacement<World>> const p10 =
      camera_origin + 1 * camera_x_axis + 2 * camera_y_axis + 3 * camera_z_axis;
  EXPECT_THAT(*perspective(p10),
              Componentwise(AlmostEquals(1.0 / 0.3 * Metre, 3),
                            AlmostEquals(2.0 / 0.3 * Metre, 2)));
}

TEST_F(PerspectiveTest, IsHiddenBySphere) {
  Perspective<World, Camera, Length, OrthogonalMap> perspective(
      AffineMap<World, Camera, Length, OrthogonalMap>::Identity(),
      /*focal=*/1 * Metre);

  Sphere<Length, World> const sphere(
      World::origin + Displacement<World>({10 * Metre, 20 * Metre, 30 * Metre}),
      /*radius=*/3 * Metre);

  // Within the sphere.
  Point<Displacement<World>> const p1 =
      World::origin +
      Displacement<World>({11 * Metre, 19 * Metre, 32 * Metre});
  // Far from the sphere.
  Point<Displacement<World>> const p2 =
      World::origin +
      Displacement<World>({100 * Metre, 50 * Metre, -70 * Metre});
  // Behind the sphere.
  Point<Displacement<World>> const p3 =
      World::origin +
      Displacement<World>({100 * Metre, 202 * Metre, 305 * Metre});
  // In front of the sphere.
  Point<Displacement<World>> const p4 =
      World::origin +
      Displacement<World>({2 * Metre, 4.05 * Metre, 6 * Metre});

  EXPECT_TRUE(perspective.IsHiddenBySphere(p1, sphere));
  EXPECT_FALSE(perspective.IsHiddenBySphere(p2, sphere));
  EXPECT_TRUE(perspective.IsHiddenBySphere(p3, sphere));
  EXPECT_FALSE(perspective.IsHiddenBySphere(p4, sphere));
}

TEST_F(PerspectiveTest, VisibleSegments) {
  Perspective<World, Camera, Length, OrthogonalMap> perspective(
      AffineMap<World, Camera, Length, OrthogonalMap>::Identity(),
      /*focal=*/1 * Metre);

  Sphere<Length, World> const sphere(
      World::origin + Displacement<World>({10 * Metre, 20 * Metre, 30 * Metre}),
      /*radius=*/3 * Metre);

  Point<Displacement<World>> const p1 =
      World::origin +
      Displacement<World>({11 * Metre, 19 * Metre, 32 * Metre});
  Point<Displacement<World>> const p2 =
      World::origin +
      Displacement<World>({100 * Metre, 50 * Metre, -70 * Metre});

  perspective.VisibleSegments({p1, p2}, sphere);
}

TEST_F(PerspectiveTest, BehindCamera) {
  Perspective<World, Camera, Length, OrthogonalMap> perspective(
      AffineMap<World, Camera, Length, OrthogonalMap>::Identity(),
      /*focal=*/1 * Metre);

  // In front of the camera.
  Point<Displacement<World>> const p1 =
      World::origin +
      Displacement<World>({1 * Metre, -2 * Metre, 3 * Metre});
  // Behind the camera.
  Point<Displacement<World>> const p2 =
      World::origin +
      Displacement<World>({-1 * Metre, 2 * Metre, -3 * Metre});

  EXPECT_TRUE(perspective(p1).has_value());
  EXPECT_FALSE(perspective(p2).has_value());
}

}  // namespace internal_perspective
}  // namespace geometry
}  // namespace principia
<commit_msg>Tests with no intersection.<commit_after>
#pragma once

#include "geometry/affine_map.hpp"
#include "geometry/frame.hpp"
#include "geometry/orthogonal_map.hpp"
#include "geometry/named_quantities.hpp"
#include "geometry/perspective.hpp"
#include "geometry/rotation.hpp"
#include "geometry/rp2_point.hpp"
#include "geometry/sphere.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/elementary_functions.hpp"
#include "quantities/quantities.hpp"
#include "quantities/si.hpp"
#include "testing_utilities/almost_equals.hpp"
#include "testing_utilities/componentwise.hpp"
#include "testing_utilities/vanishes_before.hpp"

namespace principia {
namespace geometry {
namespace internal_perspective {

using quantities::Length;
using quantities::Sqrt;
using quantities::si::Metre;
using quantities::si::Radian;
using testing_utilities::AlmostEquals;
using testing_utilities::Componentwise;
using testing_utilities::VanishesBefore;
using ::testing::Eq;
using ::testing::ElementsAre;

class PerspectiveTest : public ::testing::Test {
 protected:
  using World = Frame<serialization::Frame::TestTag,
                      serialization::Frame::TEST1, false>;
  using Camera = Frame<serialization::Frame::TestTag,
                       serialization::Frame::TEST2, false>;
};

TEST_F(PerspectiveTest, Basic) {
  Point<Displacement<World>> const camera_origin =
      World::origin + Displacement<World>({1 * Metre, 2 * Metre, -3 * Metre});
  Rotation<World, Camera> const world_to_camera_rotation(
      π / 6 * Radian,
      π / 4 * Radian,
      π / 3 * Radian,
      CardanoAngles::ZYX,
      DefinesFrame<Camera>());
  AffineMap<World, Camera, Length, OrthogonalMap> const world_to_camera_affine(
      camera_origin,
      Camera::origin,
      world_to_camera_rotation.Forget());
  Perspective<World, Camera, Length, OrthogonalMap> perspective(
      world_to_camera_affine,
      /*focal=*/10 * Metre);

  // Check that points in the camera z axis get projected to the origin of ℝP².
  Displacement<World> const camera_z_axis = world_to_camera_rotation.Inverse()(
      Displacement<Camera>({0 * Metre, 0 * Metre, 1 * Metre}));
  Point<Displacement<World>> const p0 = camera_origin;
  Point<Displacement<World>> const p1 = camera_origin + 1 * camera_z_axis;
  Point<Displacement<World>> const p2 = camera_origin + 10 * camera_z_axis;
  EXPECT_TRUE(perspective(p0)->is_at_infinity());
  EXPECT_THAT(*perspective(p1),
              Componentwise(VanishesBefore(1 * Metre, 10),
                            VanishesBefore(1 * Metre, 0)));
  EXPECT_THAT(*perspective(p2),
              Componentwise(VanishesBefore(1 * Metre, 8),
                            VanishesBefore(1 * Metre, 4)));

  // Check that points on the camera x axis get projected on the x axis of ℝP².
  Displacement<World> const camera_x_axis = world_to_camera_rotation.Inverse()(
      Displacement<Camera>({1 * Metre, 0 * Metre, 0 * Metre}));
  Point<Displacement<World>> const p3 = p1 + 5 * camera_x_axis;
  Point<Displacement<World>> const p4 = p1 + 7 * camera_x_axis;
  EXPECT_THAT(perspective(p3)->y(), VanishesBefore(1 * Metre, 20));
  EXPECT_THAT(perspective(p4)->y(), VanishesBefore(1 * Metre, 10));

  // Check that points on the camera y axis get projected on the y axis of ℝP².
  Displacement<World> const camera_y_axis = world_to_camera_rotation.Inverse()(
      Displacement<Camera>({0 * Metre, 1 * Metre, 0 * Metre}));
  Point<Displacement<World>> const p5 = p1 - 11 * camera_y_axis;
  Point<Displacement<World>> const p6 = p1 + 13 * camera_y_axis;
  EXPECT_THAT(perspective(p5)->x(), VanishesBefore(1 * Metre, 120));
  EXPECT_THAT(perspective(p6)->x(), VanishesBefore(1 * Metre, 0));

  // Check that aligned points are aligned in ℝP².
  Point<Displacement<World>> const p7 =
      camera_origin +
      Displacement<World>({17 * Metre, -23 * Metre, 29 * Metre});
  Point<Displacement<World>> const p8 =
      camera_origin +
      Displacement<World>({18 * Metre, -21 * Metre, 24 * Metre});
  Point<Displacement<World>> const p9 =
      camera_origin +
      Displacement<World>({19 * Metre, -19 * Metre, 19 * Metre});
  auto const q7 = perspective(p7);
  auto const q8 = perspective(p8);
  auto const q9 = perspective(p9);
  EXPECT_THAT((q8->x() - q7->x()) * (q9->y() - q7->y()) -
                  (q9->x() - q7->x()) * (q8->y() - q7->y()),
              VanishesBefore(1 * Metre * Metre, 6));

  // Check that the focal works as expected.
  Point<Displacement<World>> const p10 =
      camera_origin + 1 * camera_x_axis + 2 * camera_y_axis + 3 * camera_z_axis;
  EXPECT_THAT(*perspective(p10),
              Componentwise(AlmostEquals(1.0 / 0.3 * Metre, 3),
                            AlmostEquals(2.0 / 0.3 * Metre, 2)));
}

TEST_F(PerspectiveTest, IsHiddenBySphere) {
  Perspective<World, Camera, Length, OrthogonalMap> perspective(
      AffineMap<World, Camera, Length, OrthogonalMap>::Identity(),
      /*focal=*/1 * Metre);

  Sphere<Length, World> const sphere(
      World::origin + Displacement<World>({10 * Metre, 20 * Metre, 30 * Metre}),
      /*radius=*/3 * Metre);

  // Within the sphere.
  Point<Displacement<World>> const p1 =
      World::origin +
      Displacement<World>({11 * Metre, 19 * Metre, 32 * Metre});
  // Far from the sphere.
  Point<Displacement<World>> const p2 =
      World::origin +
      Displacement<World>({100 * Metre, 50 * Metre, -70 * Metre});
  // Behind the sphere.
  Point<Displacement<World>> const p3 =
      World::origin +
      Displacement<World>({100 * Metre, 202 * Metre, 305 * Metre});
  // In front of the sphere.
  Point<Displacement<World>> const p4 =
      World::origin +
      Displacement<World>({2 * Metre, 4.05 * Metre, 6 * Metre});

  EXPECT_TRUE(perspective.IsHiddenBySphere(p1, sphere));
  EXPECT_FALSE(perspective.IsHiddenBySphere(p2, sphere));
  EXPECT_TRUE(perspective.IsHiddenBySphere(p3, sphere));
  EXPECT_FALSE(perspective.IsHiddenBySphere(p4, sphere));
}

TEST_F(PerspectiveTest, VisibleSegments) {
  // The camera is on the x-axis and looks towards the positive x.
  Point<Displacement<World>> const camera_origin =
      World::origin + Displacement<World>({-10 * Metre, 0 * Metre, 0 * Metre});
  AffineMap<World, Camera, Length, OrthogonalMap> const world_to_camera_affine(
      camera_origin,
      Camera::origin,
      OrthogonalMap<World, Camera>::Identity());
  Perspective<World, Camera, Length, OrthogonalMap> perspective(
      world_to_camera_affine,
      /*focal=*/1 * Metre);

  // The sphere is at the origin and has unit radius.
  Sphere<Length, World> const sphere(World::origin,
                                     /*radius=*/1 * Metre);

  // A segment away from the sphere with x > 0.
  {
    Point<Displacement<World>> const p1 =
        World::origin +
        Displacement<World>({10 * Metre, 20 * Metre, 30 * Metre});
    Point<Displacement<World>> const p2 =
        World::origin +
        Displacement<World>({9 * Metre, 21 * Metre, 32 * Metre});
    Segment<Displacement<World>> segment{p1, p2};
    EXPECT_THAT(perspective.VisibleSegments(segment, sphere),
                ElementsAre(segment));
  }

  // A segment away from the sphere with x < 0.
  {
    Point<Displacement<World>> const p1 =
        World::origin +
        Displacement<World>({-5 * Metre, 20 * Metre, 30 * Metre});
    Point<Displacement<World>> const p2 =
        World::origin +
        Displacement<World>({-3 * Metre, 21 * Metre, 32 * Metre});
    Segment<Displacement<World>> segment{p1, p2};
    EXPECT_THAT(perspective.VisibleSegments(segment, sphere),
                ElementsAre(segment));
  }

  // A segment tangent to the sphere when seen from the camera.
  {
    double const ε = std::numeric_limits<double>::epsilon();
    Point<Displacement<World>> const p1 =
        World::origin +
        Displacement<World>(
            {9.8 * Metre, Sqrt(3.96) * (1 + ε) * Metre, 7 * Metre});
    Point<Displacement<World>> const p2 =
        World::origin +
        Displacement<World>(
            {9.8 * Metre, Sqrt(3.96) * (1 + ε) * Metre, -9 * Metre});
    Segment<Displacement<World>> segment{p1, p2};
    EXPECT_THAT(perspective.VisibleSegments(segment, sphere),
                ElementsAre(segment));
  }
}

TEST_F(PerspectiveTest, BehindCamera) {
  Perspective<World, Camera, Length, OrthogonalMap> perspective(
      AffineMap<World, Camera, Length, OrthogonalMap>::Identity(),
      /*focal=*/1 * Metre);

  // In front of the camera.
  Point<Displacement<World>> const p1 =
      World::origin +
      Displacement<World>({1 * Metre, -2 * Metre, 3 * Metre});
  // Behind the camera.
  Point<Displacement<World>> const p2 =
      World::origin +
      Displacement<World>({-1 * Metre, 2 * Metre, -3 * Metre});

  EXPECT_TRUE(perspective(p1).has_value());
  EXPECT_FALSE(perspective(p2).has_value());
}

}  // namespace internal_perspective
}  // namespace geometry
}  // namespace principia
<|endoftext|>


<commit_before>#include <stdio.h>
#include <memory.h>
#include <assert.h>
#include <stdlib.h>

#include "halide_benchmark.h"

#include "pipeline_cpu.h"
#include "pipeline_hvx64.h"
#include "pipeline_hvx128.h"

#include "HalideRuntimeHexagonHost.h"
#include "HalideBuffer.h"

template <typename T>
T clamp(T x, T min, T max) {
    if (x < min) x = min;
    if (x > max) x = max;
    return x;
}

int main(int argc, char **argv) {
    if (argc < 3) {
        printf("Usage: %s (cpu|hvx64) timing_iterations\n", argv[0]);
        return 0;
    }

    int (*pipeline)(halide_buffer_t *, halide_buffer_t*);
    if (strcmp(argv[1], "cpu") == 0) {
        pipeline = pipeline_cpu;
        printf("Using CPU schedule\n");
    } else if (strcmp(argv[1], "hvx64") == 0) {
        pipeline = pipeline_hvx64;
        printf("Using HVX 64 schedule\n");
    } else if (strcmp(argv[1], "hvx128") == 0) {
        pipeline = pipeline_hvx128;
        printf("Using HVX 128 schedule\n");
    } else {
        printf("Unknown schedule, valid schedules are cpu, hvx64, or hvx128\n");
        return -1;
    }

    int iterations = atoi(argv[2]);

    const int W = 1024;
    const int H = 1024;

    // Hexagon's device_malloc implementation will also set the host
    // pointer if it is null, giving a zero copy buffer.
    Halide::Runtime::Buffer<uint8_t> in(nullptr, W, H, 3);
    Halide::Runtime::Buffer<uint8_t> out(nullptr, W, H, 3);

    in.device_malloc(halide_hexagon_device_interface());
    out.device_malloc(halide_hexagon_device_interface());

    // Fill the input buffer with random data.
    in.for_each_value([&](uint8_t &x) {
        x = static_cast<uint8_t>(rand());
    });

    // To avoid the cost of powering HVX on in each call of the
    // pipeline, power it on once now. Also, set Hexagon performance to turbo.
    halide_hexagon_set_performance_mode(NULL, halide_hexagon_power_turbo);
    halide_hexagon_power_hvx_on(NULL);

    printf("Running pipeline...\n");
    double time = Halide::Tools::benchmark(iterations, 10, [&]() {
        int result = pipeline(in, out);
        if (result != 0) {
            printf("pipeline failed! %d\n", result);
        }
    });

    printf("Done, time: %g s\n", time);

    // We're done with HVX, power it off, and reset the performance mode
    // to default to save power.
    halide_hexagon_power_hvx_off(NULL);
    halide_hexagon_set_performance_mode(NULL, halide_hexagon_power_default);

    // Copy the output back to the host. If the buffer is zero-copy (as
    // it should be on a real device), this will be a no-op.
    out.copy_to_host();

    // Validate that the algorithm did what we expect.
    const uint16_t gaussian5[] = { 1, 4, 6, 4, 1 };
    out.for_each_element([&](int x, int y, int c) {
        uint16_t blur = 0;
        for (int rx = -2; rx <= 2; rx++) {
            uint16_t blur_y = 0;
            for (int ry = -2; ry <= 2; ry++) {
                uint16_t in_rxy =
                    in(clamp(x + rx, 0, W - 1), clamp(y + ry, 0, H - 1), c);
                blur_y += in_rxy * gaussian5[ry + 2];
            }
            blur_y += 8;
            blur_y /= 16;

            blur += blur_y * gaussian5[rx + 2];
        }
        blur += 8;
        blur /= 16;

        uint8_t out_xy = out(x, y, c);
        if (blur != out_xy) {
            printf("Mismatch at %d %d %d: %d != %d\n", x, y, c, out_xy, blur);
            abort();
        }

    });

    printf("Success!\n");

    return 0;
}
<commit_msg>Fix names in HelloHexagon<commit_after>#include <stdio.h>
#include <memory.h>
#include <assert.h>
#include <stdlib.h>

#include "halide_benchmark.h"

#include "blur_cpu.h"
#include "blur_hvx64.h"
#include "blur_hvx128.h"

#include "HalideRuntimeHexagonHost.h"
#include "HalideBuffer.h"

template <typename T>
T clamp(T x, T min, T max) {
    if (x < min) x = min;
    if (x > max) x = max;
    return x;
}

int main(int argc, char **argv) {
    if (argc < 3) {
        printf("Usage: %s (cpu|hvx64) timing_iterations\n", argv[0]);
        return 0;
    }

    int (*pipeline)(halide_buffer_t *, halide_buffer_t*);
    if (strcmp(argv[1], "cpu") == 0) {
        pipeline = blur_cpu;
        printf("Using CPU schedule\n");
    } else if (strcmp(argv[1], "hvx64") == 0) {
        pipeline = blur_hvx64;
        printf("Using HVX 64 schedule\n");
    } else if (strcmp(argv[1], "hvx128") == 0) {
        pipeline = blur_hvx128;
        printf("Using HVX 128 schedule\n");
    } else {
        printf("Unknown schedule, valid schedules are cpu, hvx64, or hvx128\n");
        return -1;
    }

    int iterations = atoi(argv[2]);

    const int W = 1024;
    const int H = 1024;

    // Hexagon's device_malloc implementation will also set the host
    // pointer if it is null, giving a zero copy buffer.
    Halide::Runtime::Buffer<uint8_t> in(nullptr, W, H, 3);
    Halide::Runtime::Buffer<uint8_t> out(nullptr, W, H, 3);

    in.device_malloc(halide_hexagon_device_interface());
    out.device_malloc(halide_hexagon_device_interface());

    // Fill the input buffer with random data.
    in.for_each_value([&](uint8_t &x) {
        x = static_cast<uint8_t>(rand());
    });

    // To avoid the cost of powering HVX on in each call of the
    // pipeline, power it on once now. Also, set Hexagon performance to turbo.
    halide_hexagon_set_performance_mode(NULL, halide_hexagon_power_turbo);
    halide_hexagon_power_hvx_on(NULL);

    printf("Running pipeline...\n");
    double time = Halide::Tools::benchmark(iterations, 10, [&]() {
        int result = pipeline(in, out);
        if (result != 0) {
            printf("pipeline failed! %d\n", result);
        }
    });

    printf("Done, time: %g s\n", time);

    // We're done with HVX, power it off, and reset the performance mode
    // to default to save power.
    halide_hexagon_power_hvx_off(NULL);
    halide_hexagon_set_performance_mode(NULL, halide_hexagon_power_default);

    // Copy the output back to the host. If the buffer is zero-copy (as
    // it should be on a real device), this will be a no-op.
    out.copy_to_host();

    // Validate that the algorithm did what we expect.
    const uint16_t gaussian5[] = { 1, 4, 6, 4, 1 };
    out.for_each_element([&](int x, int y, int c) {
        uint16_t blur = 0;
        for (int rx = -2; rx <= 2; rx++) {
            uint16_t blur_y = 0;
            for (int ry = -2; ry <= 2; ry++) {
                uint16_t in_rxy =
                    in(clamp(x + rx, 0, W - 1), clamp(y + ry, 0, H - 1), c);
                blur_y += in_rxy * gaussian5[ry + 2];
            }
            blur_y += 8;
            blur_y /= 16;

            blur += blur_y * gaussian5[rx + 2];
        }
        blur += 8;
        blur /= 16;

        uint8_t out_xy = out(x, y, c);
        if (blur != out_xy) {
            printf("Mismatch at %d %d %d: %d != %d\n", x, y, c, out_xy, blur);
            abort();
        }

    });

    printf("Success!\n");

    return 0;
}
<|endoftext|>


<commit_before>#include <string>
#include <climits>
#include <iostream>
#include <vector>
#include <unordered_map>
#include <set>
#include <algorithm>

using namespace std;

class Solution {
public:
    int mySqrt(int x) {
        return 0;
    }
};

void showAns(int x, int ans) {
	cout << "sqrt(" << x << ") = " << ans << endl;
}

int main() {
    Solution s;
	int x;
	int ans;

	x = 0;
	ans = s.mySqrt(x);
	showAns(x, ans);

    return 0;
}
<commit_msg>leet: 69. modified<commit_after>#include <string>
#include <climits>
#include <iostream>
#include <vector>
#include <unordered_map>
#include <set>
#include <algorithm>

using namespace std;

class Solution {
public:
    int mySqrt(int x) {
        int t = x;
        while (t > 100) {
            t / 100;
        };
        cout << t << endl;;
        return 0;
    }
};

void showAns(int x, int ans) {
	cout << "sqrt(" << x << ") = " << ans << endl;
}

int main() {
    Solution s;
	int x;
	int ans;

	x = 0;
	ans = s.mySqrt(x);
	showAns(x, ans);

	x = 99;
	ans = s.mySqrt(x);
	showAns(x, ans);

    return 0;
}
<|endoftext|>


<commit_before>#ifndef HEADER_GUARD_CLASS_CONTENTS_H
#define HEADER_GUARD_CLASS_CONTENTS_H

#include <vector>
#include <string>
#include <memory>

#include "change.hh"
#include "mode.hh"

namespace vick {

/*!
 * \file contents.hh
 *
 * \brief Defines the class contents, which contains all the
 * information about the buffer.
 */

/*!
 * \brief Define \c move_t so that code can be more descriptive and
 * can change it from one place.
 */
using move_t = unsigned long;

/*!
 * \brief Define \c move_ts to be a signed version of \c move_t
 *
 * \see move_t
 */
using move_ts = long;

/*!
 * \class contents contents.hh "../../../src/contents.hh"
 *
 * \brief Defines all the information about the buffer.
 */
class contents {
  public:
    /*!
     * \brief The type of file the buffer is.
     *
     * Default mode is fundamental;
     */
    mode* buffer_mode;
    /*!
     * \brief The literal contents of the buffer
     */
    std::vector<std::string> cont;
    /*!
     * \brief The list of changes to the buffer.  This is public so
     * that treed-undo (a la Emacs) can be implemented the same as
     * linear is.
     */
    std::vector<std::shared_ptr<change> > changes;
    /*!
     * \brief The current change the buffer is in
     */
    size_t changes_i = 0;
    move_t
        y = 0 /*!< \brief The y (vertical) position in the buffer. */,
        x = 0 /*!< \brief The x (horizontal) position in the buffer. */,
        desired_x = 0 /*!< \brief This will be set to the x value we
                       * want when you move to a different line but
                       * the line you move to is too short
                       *
                       * \see waiting_for_desired */,
        y_offset = 0 /*!< \brief The number of lines that are not
                      * displayed (off the top) */,
        max_y /*!< \brief The vertical height the buffer has been allocated */,
        max_x /*!< \brief The horizontal width the buffer has been allocated */;
    bool waiting_for_desired = false /*!< \brief Controls if the x
                                      * value will try to adjust to
                                      * desired_x
                                      *
                                      * \see desired_x */,
         refresh = true /*!< \brief Controls if the screen should
                         * refresh everytime something happens */,
         delete_mode = false /*!< \brief Controls if the private mode
                              * pointer variable will be deleted in the
                              * destructor */,
         is_inserting = false /*!< \brief Will be true if the user is
                               * currently inserting text into the
                               * buffer.  This will cause the
                               * print_contents method to not have
                               * undefined behavior if x is too
                               * large. */,
        windows_file_endings = false /*!< \brief If true, then when
                                      * saving, appends the byte 13 to
                                      * each line.  We use this to
                                      * maintain plugin consistency
                                      * across platforms.  (The buffer
                                      * is in no way affected by this
                                      * variable)
                                      */;

    /*!
     * Construcs a contents object
     */
    explicit contents(std::vector<std::string> cont = std::vector<std::string>(),
             mode* buffer_mode = &fundamental_mode);
    /*!
     * Constructs a contents object
     */
    explicit contents(mode* buffer_mode);
    /*!
     * Constructs a contents object
     */
    contents(move_t y, move_t x, mode* buffer_mode = &fundamental_mode);

    /*!
     * \brief Calls operator() on the private mode pointer member.
     *
     * \see mode
     */
    bool operator()(char) const;

    /*!
     * \brief If delete_mode is true, then it will delete the private
     * mode pointer member.
     *
     * \see delete_mode
     */
    ~contents();
    /*!
     * \brief Constructs this contents object as a copy of the other
     * contents object.
     *
     * This forces the private mode member to make a deep copy.
     */
    contents(const contents&);
    /*!
     * \brief Constructs this contents object as a copy of the other
     * contents object.
     */
    contents(contents&&);
    /*!
     * \brief Constructs this contents object as a copy of the other
     * contents object.
     *
     * This forces the private mode member to make a deep copy.
     */
    contents& operator=(const contents&);
    /*!
     * \brief Constructs this contents object as a copy of the other
     * contents object.
     */
    contents& operator=(contents&&);

    /*!
     * \brief Updates the values of ``max_y`` and ``max_x``
     *
     * \see max_y
     * \see max_x
     */
    void refreshmaxyx();

    /*!
     * \brief Calls ``cont.push_back(str)``
     *
     * \see cont
     *
     * \param str The string to be put at the end of the vector of strings, cont
     */
    void push_back(const std::string& str);
};

}

#endif
<commit_msg>Put line separators between functions in headers.<commit_after>#ifndef HEADER_GUARD_CLASS_CONTENTS_H
#define HEADER_GUARD_CLASS_CONTENTS_H

#include <vector>
#include <string>
#include <memory>

#include "change.hh"
#include "mode.hh"

namespace vick {

/*!
 * \file contents.hh
 *
 * \brief Defines the class contents, which contains all the
 * information about the buffer.
 */

/*!
 * \brief Define \c move_t so that code can be more descriptive and
 * can change it from one place.
 */
using move_t = unsigned long;

/*!
 * \brief Define \c move_ts to be a signed version of \c move_t
 *
 * \see move_t
 */
using move_ts = long;

/*!
 * \class contents contents.hh "../../../src/contents.hh"
 *
 * \brief Defines all the information about the buffer.
 */
class contents {
  public:
    /*!
     * \brief The type of file the buffer is.
     *
     * Default mode is fundamental;
     */
    mode* buffer_mode;
    /*!
     * \brief The literal contents of the buffer
     */
    std::vector<std::string> cont;
    /*!
     * \brief The list of changes to the buffer.  This is public so
     * that treed-undo (a la Emacs) can be implemented the same as
     * linear is.
     */
    std::vector<std::shared_ptr<change> > changes;
    /*!
     * \brief The current change the buffer is in
     */
    size_t changes_i = 0;
    move_t
        y = 0 /*!< \brief The y (vertical) position in the buffer. */,
        x = 0 /*!< \brief The x (horizontal) position in the buffer. */,
        desired_x = 0 /*!< \brief This will be set to the x value we
                       * want when you move to a different line but
                       * the line you move to is too short
                       *
                       * \see waiting_for_desired */,
        y_offset = 0 /*!< \brief The number of lines that are not
                      * displayed (off the top) */,
        max_y /*!< \brief The vertical height the buffer has been allocated */,
        max_x /*!< \brief The horizontal width the buffer has been allocated */;
    bool waiting_for_desired = false /*!< \brief Controls if the x
                                      * value will try to adjust to
                                      * desired_x
                                      *
                                      * \see desired_x */,
         refresh = true /*!< \brief Controls if the screen should
                         * refresh everytime something happens */,
         delete_mode = false /*!< \brief Controls if the private mode
                              * pointer variable will be deleted in the
                              * destructor */,
         is_inserting = false /*!< \brief Will be true if the user is
                               * currently inserting text into the
                               * buffer.  This will cause the
                               * print_contents method to not have
                               * undefined behavior if x is too
                               * large. */,
        windows_file_endings = false /*!< \brief If true, then when
                                      * saving, appends the byte 13 to
                                      * each line.  We use this to
                                      * maintain plugin consistency
                                      * across platforms.  (The buffer
                                      * is in no way affected by this
                                      * variable)
                                      */;

    /*!
     * Construcs a contents object
     */
    explicit contents(std::vector<std::string> cont = std::vector<std::string>(),
             mode* buffer_mode = &fundamental_mode);

    /*!
     * Constructs a contents object
     */
    explicit contents(mode* buffer_mode);

    /*!
     * Constructs a contents object
     */
    contents(move_t y, move_t x, mode* buffer_mode = &fundamental_mode);

    /*!
     * \brief Calls operator() on the private mode pointer member.
     *
     * \see mode
     */
    bool operator()(char) const;

    /*!
     * \brief If delete_mode is true, then it will delete the private
     * mode pointer member.
     *
     * \see delete_mode
     */
    ~contents();

    /*!
     * \brief Constructs this contents object as a copy of the other
     * contents object.
     *
     * This forces the private mode member to make a deep copy.
     */
    contents(const contents&);

    /*!
     * \brief Constructs this contents object as a copy of the other
     * contents object.
     */
    contents(contents&&);

    /*!
     * \brief Constructs this contents object as a copy of the other
     * contents object.
     *
     * This forces the private mode member to make a deep copy.
     */
    contents& operator=(const contents&);

    /*!
     * \brief Constructs this contents object as a copy of the other
     * contents object.
     */
    contents& operator=(contents&&);

    /*!
     * \brief Updates the values of ``max_y`` and ``max_x``
     *
     * \see max_y
     * \see max_x
     */
    void refreshmaxyx();

    /*!
     * \brief Calls ``cont.push_back(str)``
     *
     * \see cont
     *
     * \param str The string to be put at the end of the vector of strings, cont
     */
    void push_back(const std::string& str);
};

}

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2010 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 "chrome/browser/extensions/extension_apitest.h"
#include "net/base/mock_host_resolver.h"

IN_PROC_BROWSER_TEST_F(ExtensionApiTest, JavaScriptURLPermissions) {
  host_resolver()->AddRule("a.com", "127.0.0.1");
  host_resolver()->AddRule("b.com", "127.0.0.1");
  ASSERT_TRUE(StartTestServer());

  ASSERT_TRUE(RunExtensionTest("tabs/javascript_url_permissions")) << message_;
}
<commit_msg>Disable ExtensionApiTest.JavaScriptURLPermissions, it flakily hits an assertion.<commit_after>// Copyright (c) 2010 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 "chrome/browser/extensions/extension_apitest.h"
#include "net/base/mock_host_resolver.h"

// Disabled, http://crbug.com/63589.
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_JavaScriptURLPermissions) {
  host_resolver()->AddRule("a.com", "127.0.0.1");
  host_resolver()->AddRule("b.com", "127.0.0.1");
  ASSERT_TRUE(StartTestServer());

  ASSERT_TRUE(RunExtensionTest("tabs/javascript_url_permissions")) << message_;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 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 "chrome/test/live_sync/live_extensions_sync_test.h"

#include "base/basictypes.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension.h"

class TwoClientLiveExtensionsSyncTest : public LiveExtensionsSyncTest {
 public:
  TwoClientLiveExtensionsSyncTest()
      : LiveExtensionsSyncTest(TWO_CLIENT) {}

  virtual ~TwoClientLiveExtensionsSyncTest() {}

 private:
  DISALLOW_COPY_AND_ASSIGN(TwoClientLiveExtensionsSyncTest);
};

IN_PROC_BROWSER_TEST_F(TwoClientLiveExtensionsSyncTest,
                       StartWithNoExtensions) {
  ASSERT_TRUE(SetupSync());

  ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
}

IN_PROC_BROWSER_TEST_F(TwoClientLiveExtensionsSyncTest,
                       StartWithSameExtensions) {
  ASSERT_TRUE(SetupClients());

  const int kNumExtensions = 5;
  for (int i = 0; i < kNumExtensions; ++i) {
    InstallExtension(GetProfile(0), GetExtension(i));
    InstallExtension(GetProfile(1), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  ASSERT_TRUE(SetupSync());

  ASSERT_TRUE(AwaitQuiescence());

  ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
}

IN_PROC_BROWSER_TEST_F(TwoClientLiveExtensionsSyncTest,
                       StartWithDifferentExtensions) {
  ASSERT_TRUE(SetupClients());

  int i = 0;

  const int kNumCommonExtensions = 5;
  for (int j = 0; j < kNumCommonExtensions; ++i, ++j) {
    InstallExtension(GetProfile(0), GetExtension(i));
    InstallExtension(GetProfile(1), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  const int kNumProfile0Extensions = 10;
  for (int j = 0; j < kNumProfile0Extensions; ++i, ++j) {
    InstallExtension(GetProfile(0), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  const int kNumProfile1Extensions = 10;
  for (int j = 0; j < kNumProfile1Extensions; ++i, ++j) {
    InstallExtension(GetProfile(1), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  ASSERT_TRUE(SetupSync());

  ASSERT_TRUE(AwaitQuiescence());

  InstallAllPendingExtensions(GetProfile(0));
  InstallAllPendingExtensions(GetProfile(1));

  ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
}

IN_PROC_BROWSER_TEST_F(TwoClientLiveExtensionsSyncTest,
                       InstallDifferentExtensions) {
  ASSERT_TRUE(SetupClients());

  int i = 0;

  const int kNumCommonExtensions = 5;
  for (int j = 0; j < kNumCommonExtensions; ++i, ++j) {
    InstallExtension(GetProfile(0), GetExtension(i));
    InstallExtension(GetProfile(1), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  ASSERT_TRUE(SetupSync());

  ASSERT_TRUE(AwaitQuiescence());

  const int kNumProfile0Extensions = 10;
  for (int j = 0; j < kNumProfile0Extensions; ++i, ++j) {
    InstallExtension(GetProfile(0), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  const int kNumProfile1Extensions = 10;
  for (int j = 0; j < kNumProfile1Extensions; ++i, ++j) {
    InstallExtension(GetProfile(1), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  ASSERT_TRUE(AwaitQuiescence());

  InstallAllPendingExtensions(GetProfile(0));
  InstallAllPendingExtensions(GetProfile(1));

  ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
}

// TODO(akalin): Add tests exercising:
//   - Extensions enabled/disabled state sync behavior
//   - Extension uninstallation
//   - Offline installation/uninstallation behavior
<commit_msg>Annotating a couple of failing extensions sync integration tests as FAILS<commit_after>// Copyright (c) 2010 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 "chrome/test/live_sync/live_extensions_sync_test.h"

#include "base/basictypes.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/extension.h"

class TwoClientLiveExtensionsSyncTest : public LiveExtensionsSyncTest {
 public:
  TwoClientLiveExtensionsSyncTest()
      : LiveExtensionsSyncTest(TWO_CLIENT) {}

  virtual ~TwoClientLiveExtensionsSyncTest() {}

 private:
  DISALLOW_COPY_AND_ASSIGN(TwoClientLiveExtensionsSyncTest);
};

IN_PROC_BROWSER_TEST_F(TwoClientLiveExtensionsSyncTest,
                       StartWithNoExtensions) {
  ASSERT_TRUE(SetupSync());

  ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
}

IN_PROC_BROWSER_TEST_F(TwoClientLiveExtensionsSyncTest,
                       StartWithSameExtensions) {
  ASSERT_TRUE(SetupClients());

  const int kNumExtensions = 5;
  for (int i = 0; i < kNumExtensions; ++i) {
    InstallExtension(GetProfile(0), GetExtension(i));
    InstallExtension(GetProfile(1), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  ASSERT_TRUE(SetupSync());

  ASSERT_TRUE(AwaitQuiescence());

  ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
}

// TODO(rsimha): Remove FAILS_ prefix after http://crbug.com/66925 is fixed.
IN_PROC_BROWSER_TEST_F(TwoClientLiveExtensionsSyncTest,
                       FAILS_StartWithDifferentExtensions) {
  ASSERT_TRUE(SetupClients());

  int i = 0;

  const int kNumCommonExtensions = 5;
  for (int j = 0; j < kNumCommonExtensions; ++i, ++j) {
    InstallExtension(GetProfile(0), GetExtension(i));
    InstallExtension(GetProfile(1), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  const int kNumProfile0Extensions = 10;
  for (int j = 0; j < kNumProfile0Extensions; ++i, ++j) {
    InstallExtension(GetProfile(0), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  const int kNumProfile1Extensions = 10;
  for (int j = 0; j < kNumProfile1Extensions; ++i, ++j) {
    InstallExtension(GetProfile(1), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  ASSERT_TRUE(SetupSync());

  ASSERT_TRUE(AwaitQuiescence());

  InstallAllPendingExtensions(GetProfile(0));
  InstallAllPendingExtensions(GetProfile(1));

  ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
}

// TODO(rsimha): Remove FAILS_ prefix after http://crbug.com/66925 is fixed.
IN_PROC_BROWSER_TEST_F(TwoClientLiveExtensionsSyncTest,
                       FAILS_InstallDifferentExtensions) {
  ASSERT_TRUE(SetupClients());

  int i = 0;

  const int kNumCommonExtensions = 5;
  for (int j = 0; j < kNumCommonExtensions; ++i, ++j) {
    InstallExtension(GetProfile(0), GetExtension(i));
    InstallExtension(GetProfile(1), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  ASSERT_TRUE(SetupSync());

  ASSERT_TRUE(AwaitQuiescence());

  const int kNumProfile0Extensions = 10;
  for (int j = 0; j < kNumProfile0Extensions; ++i, ++j) {
    InstallExtension(GetProfile(0), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  const int kNumProfile1Extensions = 10;
  for (int j = 0; j < kNumProfile1Extensions; ++i, ++j) {
    InstallExtension(GetProfile(1), GetExtension(i));
    InstallExtension(verifier(), GetExtension(i));
  }

  ASSERT_TRUE(AwaitQuiescence());

  InstallAllPendingExtensions(GetProfile(0));
  InstallAllPendingExtensions(GetProfile(1));

  ASSERT_TRUE(AllProfilesHaveSameExtensionsAsVerifier());
}

// TODO(akalin): Add tests exercising:
//   - Extensions enabled/disabled state sync behavior
//   - Extension uninstallation
//   - Offline installation/uninstallation behavior
<|endoftext|>