<commit_before>#include "Atm_encoder.hpp"
#include <limits.h>

// Loosely based on https://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino (Oleg Mazurov)

const char Atm_encoder::_enc_states[16] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};

Atm_encoder& Atm_encoder::begin( int pin1, int pin2, int divider /* = 1 */ ) {
  // clang-format off
  const static state_t state_table[] PROGMEM = {
    /*          ON_ENTER     ON_LOOP  ON_EXIT  EVT_UP  EVT_DOWN  ELSE */
    /* IDLE  */       -1, ACT_SAMPLE,      -1,     UP,     DOWN,   -1,
    /* UP    */   ACT_UP,         -1,      -1,     -1,       -1, IDLE,
    /* DOWN  */ ACT_DOWN,         -1,      -1,     -1,       -1, IDLE,
  };
  // clang-format on
  Machine::begin( state_table, ELSE );
  _pin1 = pin1;
  _pin2 = pin2;
  _divider = divider;
  pinMode( _pin1, INPUT );
  pinMode( _pin2, INPUT );
  digitalWrite( _pin1, HIGH );
  digitalWrite( _pin2, HIGH );
  _min = INT_MIN;
  _max = INT_MAX;
  _value = 0;
  return *this;
}

Atm_encoder& Atm_encoder::range( int min, int max, bool wrap /* = false */ ) {
  _min = min;
  _max = max;
  _wrap = wrap;
  if ( _value < _min || _value > _max ) {
    _value = min;
  }
  return *this;
}

Atm_encoder& Atm_encoder::set( int value ) {
  _value = value;
  return *this;
}

Atm_encoder& Atm_encoder::onChange( Machine& machine, int event /* = 0 */ ) {
  _onup.set( &machine, event );
  _ondown.set( &machine, event );
  return *this;
}

Atm_encoder& Atm_encoder::onChange( atm_cb_push_t callback, int idx /* = 0 */ ) {
  _onup.set( callback, idx );
  _ondown.set( &machine, event );
  return *this;
}

Atm_encoder& Atm_encoder::onUp( Machine& machine, int event /* = 0 */ ) {
  _onup.set( &machine, event );
  return *this;
}

Atm_encoder& Atm_encoder::onUp( atm_cb_push_t callback, int idx /* = 0 */ ) {
  _onup.set( callback, idx );
  return *this;
}

Atm_encoder& Atm_encoder::onDown( Machine& machine, int event /* = 0 */ ) {
  _ondown.set( &machine, event );
  return *this;
}

Atm_encoder& Atm_encoder::onDown( atm_cb_push_t callback, int idx /* = 0 */ ) {
  _ondown.set( callback, idx );
  return *this;
}

int Atm_encoder::state( void ) {
  return _value;
}

int Atm_encoder::event( int id ) {
  switch ( id ) {
    case EVT_UP:
      return _enc_direction == +1 && ( _enc_counter % _divider == 0 );
    case EVT_DOWN:
      return _enc_direction == -1 && ( _enc_counter % _divider == 0 );
  }
  return 0;
}

bool Atm_encoder::count( int direction ) {
  if ( (long)_value + direction > _max ) {
    if ( _wrap ) {
      _value = _min;
    } else {
      return false;
    }
  } else if ( (long)_value + direction < _min ) {
    if ( _wrap ) {
      _value = _max;
    } else {
      return false;
    }
  } else {
    _value += direction;
  }
  return true;
}

void Atm_encoder::action( int id ) {
  switch ( id ) {
    case ACT_SAMPLE:
      _enc_bits = ( ( _enc_bits << 2 ) | ( digitalRead( _pin1 ) << 1 ) | ( digitalRead( _pin2 ) ) ) & 0x0f;
      _enc_direction = _enc_states[_enc_bits];
      if ( _enc_direction != 0 ) {
        if ( ++_enc_counter % _divider == 0 ) {
          if ( !count( _enc_direction ) ) {
            _enc_direction = 0;
          }
        }
      }
      return;
    case ACT_UP:
      _onup.push();
      return;
    case ACT_DOWN:
      _ondown.push();
      return;
  }
}

Atm_encoder& Atm_encoder::trace( Stream& stream ) {
  Machine::setTrace( &stream, atm_serial_debug::trace, "ENCODER\0EVT_UP\0EVT_DOWN\0ELSE\0IDLE\0UP\0DOWN" );
  return *this;
}
<commit_msg>Fixed onChange()<commit_after>#include "Atm_encoder.hpp"
#include <limits.h>

// Loosely based on https://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino (Oleg Mazurov)

const char Atm_encoder::_enc_states[16] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};

Atm_encoder& Atm_encoder::begin( int pin1, int pin2, int divider /* = 1 */ ) {
  // clang-format off
  const static state_t state_table[] PROGMEM = {
    /*          ON_ENTER     ON_LOOP  ON_EXIT  EVT_UP  EVT_DOWN  ELSE */
    /* IDLE  */       -1, ACT_SAMPLE,      -1,     UP,     DOWN,   -1,
    /* UP    */   ACT_UP,         -1,      -1,     -1,       -1, IDLE,
    /* DOWN  */ ACT_DOWN,         -1,      -1,     -1,       -1, IDLE,
  };
  // clang-format on
  Machine::begin( state_table, ELSE );
  _pin1 = pin1;
  _pin2 = pin2;
  _divider = divider;
  pinMode( _pin1, INPUT );
  pinMode( _pin2, INPUT );
  digitalWrite( _pin1, HIGH );
  digitalWrite( _pin2, HIGH );
  _min = INT_MIN;
  _max = INT_MAX;
  _value = 0;
  return *this;
}

Atm_encoder& Atm_encoder::range( int min, int max, bool wrap /* = false */ ) {
  _min = min;
  _max = max;
  _wrap = wrap;
  if ( _value < _min || _value > _max ) {
    _value = min;
  }
  return *this;
}

Atm_encoder& Atm_encoder::set( int value ) {
  _value = value;
  return *this;
}

Atm_encoder& Atm_encoder::onChange( Machine& machine, int event /* = 0 */ ) {
  _onup.set( &machine, event );
  _ondown.set( &machine, event );
  return *this;
}

Atm_encoder& Atm_encoder::onChange( atm_cb_push_t callback, int idx /* = 0 */ ) {
  _onup.set( callback, idx );
  _ondown.set( callback, idx );
  return *this;
}

Atm_encoder& Atm_encoder::onUp( Machine& machine, int event /* = 0 */ ) {
  _onup.set( &machine, event );
  return *this;
}

Atm_encoder& Atm_encoder::onUp( atm_cb_push_t callback, int idx /* = 0 */ ) {
  _onup.set( callback, idx );
  return *this;
}

Atm_encoder& Atm_encoder::onDown( Machine& machine, int event /* = 0 */ ) {
  _ondown.set( &machine, event );
  return *this;
}

Atm_encoder& Atm_encoder::onDown( atm_cb_push_t callback, int idx /* = 0 */ ) {
  _ondown.set( callback, idx );
  return *this;
}

int Atm_encoder::state( void ) {
  return _value;
}

int Atm_encoder::event( int id ) {
  switch ( id ) {
    case EVT_UP:
      return _enc_direction == +1 && ( _enc_counter % _divider == 0 );
    case EVT_DOWN:
      return _enc_direction == -1 && ( _enc_counter % _divider == 0 );
  }
  return 0;
}

bool Atm_encoder::count( int direction ) {
  if ( (long)_value + direction > _max ) {
    if ( _wrap ) {
      _value = _min;
    } else {
      return false;
    }
  } else if ( (long)_value + direction < _min ) {
    if ( _wrap ) {
      _value = _max;
    } else {
      return false;
    }
  } else {
    _value += direction;
  }
  return true;
}

void Atm_encoder::action( int id ) {
  switch ( id ) {
    case ACT_SAMPLE:
      _enc_bits = ( ( _enc_bits << 2 ) | ( digitalRead( _pin1 ) << 1 ) | ( digitalRead( _pin2 ) ) ) & 0x0f;
      _enc_direction = _enc_states[_enc_bits];
      if ( _enc_direction != 0 ) {
        if ( ++_enc_counter % _divider == 0 ) {
          if ( !count( _enc_direction ) ) {
            _enc_direction = 0;
          }
        }
      }
      return;
    case ACT_UP:
      _onup.push();
      return;
    case ACT_DOWN:
      _ondown.push();
      return;
  }
}

Atm_encoder& Atm_encoder::trace( Stream& stream ) {
  Machine::setTrace( &stream, atm_serial_debug::trace, "ENCODER\0EVT_UP\0EVT_DOWN\0ELSE\0IDLE\0UP\0DOWN" );
  return *this;
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "winshutdownmonitor.h"

#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
#include "init.h"
#include "util.h"

#include <windows.h>

#include <QDebug>

#include <openssl/rand.h>

// If we don't want a message to be processed by Qt, return true and set result to
// the value that the window procedure should return. Otherwise return false.
bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pnResult)
{
       Q_UNUSED(eventType);

       MSG *pMsg = static_cast<MSG *>(pMessage);

       // Seed OpenSSL PRNG with Windows event data (e.g.  mouse movements and other user interactions)
       if (RAND_event(pMsg->message, pMsg->wParam, pMsg->lParam) == 0) {
            // Warn only once as this is performance-critical
            static bool warned = false;
            if (!warned) {
                LogPrint("%s: OpenSSL RAND_event() failed to seed OpenSSL PRNG with enough data.\n", __func__);
                warned = true;
            }
       }

       switch(pMsg->message)
       {
           case WM_QUERYENDSESSION:
           {
               // Initiate a client shutdown after receiving a WM_QUERYENDSESSION and block
               // Windows session end until we have finished client shutdown.
               StartShutdown();
               *pnResult = FALSE;
               return true;
           }

           case WM_ENDSESSION:
           {
               *pnResult = FALSE;
               return true;
           }
       }

       return false;
}

void WinShutdownMonitor::registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId)
{
    typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR);
    PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate");
    if (shutdownBRCreate == NULL) {
        qWarning() << "registerShutdownBlockReason: GetProcAddress for ShutdownBlockReasonCreate failed";
        return;
    }

    if (shutdownBRCreate(mainWinId, strReason.toStdWString().c_str()))
        qWarning() << "registerShutdownBlockReason: Successfully registered: " + strReason;
    else
        qWarning() << "registerShutdownBlockReason: Failed to register: " + strReason;
}
#endif
<commit_msg>Fix LogPrint to LogPrintf<commit_after>// Copyright (c) 2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "winshutdownmonitor.h"

#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
#include "init.h"
#include "util.h"

#include <windows.h>

#include <QDebug>

#include <openssl/rand.h>

// If we don't want a message to be processed by Qt, return true and set result to
// the value that the window procedure should return. Otherwise return false.
bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pnResult)
{
       Q_UNUSED(eventType);

       MSG *pMsg = static_cast<MSG *>(pMessage);

       // Seed OpenSSL PRNG with Windows event data (e.g.  mouse movements and other user interactions)
       if (RAND_event(pMsg->message, pMsg->wParam, pMsg->lParam) == 0) {
            // Warn only once as this is performance-critical
            static bool warned = false;
            if (!warned) {
                LogPrintf("%s: OpenSSL RAND_event() failed to seed OpenSSL PRNG with enough data.\n", __func__);
                warned = true;
            }
       }

       switch(pMsg->message)
       {
           case WM_QUERYENDSESSION:
           {
               // Initiate a client shutdown after receiving a WM_QUERYENDSESSION and block
               // Windows session end until we have finished client shutdown.
               StartShutdown();
               *pnResult = FALSE;
               return true;
           }

           case WM_ENDSESSION:
           {
               *pnResult = FALSE;
               return true;
           }
       }

       return false;
}

void WinShutdownMonitor::registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId)
{
    typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR);
    PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate");
    if (shutdownBRCreate == NULL) {
        qWarning() << "registerShutdownBlockReason: GetProcAddress for ShutdownBlockReasonCreate failed";
        return;
    }

    if (shutdownBRCreate(mainWinId, strReason.toStdWString().c_str()))
        qWarning() << "registerShutdownBlockReason: Successfully registered: " + strReason;
    else
        qWarning() << "registerShutdownBlockReason: Failed to register: " + strReason;
}
#endif
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * REALM CONFIDENTIAL
 * __________________
 *
 *  [2011] - [2015] Realm Inc
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Realm Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Realm Incorporated
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Realm Incorporated.
 *
 **************************************************************************/
#ifndef REALM_COLUMN_DATETIME_HPP
#define REALM_COLUMN_DATETIME_HPP

#include <realm/column.hpp>

namespace realm {

struct NewDate {
    NewDate(int64_t seconds, int32_t nanoseconds) : m_seconds(seconds), m_nanoseconds(nanoseconds), m_is_null(false) { }
    NewDate(const null&) : m_is_null(true) { }
    NewDate() : NewDate(null()) { }

    bool is_null() const { return m_is_null; }
    bool operator == (const NewDate& rhs) const { return m_seconds == rhs.m_seconds && m_nanoseconds == rhs.m_nanoseconds; }
    bool operator != (const NewDate& rhs) const { return m_seconds != rhs.m_seconds || m_nanoseconds != rhs.m_nanoseconds; }
    bool operator > (const NewDate& rhs) const { return (m_seconds > rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_seconds > rhs.m_nanoseconds); }
    bool operator < (const NewDate& rhs) const { return (m_seconds < rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_seconds < rhs.m_nanoseconds); }
    NewDate& operator = (const NewDate& rhs) = default;

    int64_t m_seconds;
    int32_t m_nanoseconds;
    bool m_is_null;
};

class DateTimeColumn : public ColumnBase {
public:
    DateTimeColumn() : m_seconds(Allocator::get_default(), IntNullColumn::create(Allocator::get_default())),
                       m_nanoseconds(Allocator::get_default(), IntegerColumn::create(Allocator::get_default()))
    {
    }

    /// Get the number of entries in this column. This operation is relatively
    /// slow.
    size_t size() const noexcept override {
        // FIXME: Consider debug asserts on the columns having the same size
        return m_seconds.size();
    }

    /// Whether or not this column is nullable.
    bool is_nullable() const noexcept override {
        return m_seconds.is_nullable();
    }

    /// Whether or not the value at \a row_ndx is NULL. If the column is not
    /// nullable, always returns false.
    bool is_null(size_t row_ndx) const noexcept override {
        return m_seconds.is_null(row_ndx);
    }

    /// Sets the value at \a row_ndx to be NULL.
    /// \throw LogicError Thrown if this column is not nullable.
    void set_null(size_t row_ndx) override {
        m_seconds.set_null(row_ndx);
    }

    void insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool nullable) override {
        m_seconds.insert_rows(row_ndx, num_rows_to_insert, prior_num_rows, nullable);
        m_nanoseconds.insert_rows(row_ndx, num_rows_to_insert, prior_num_rows, nullable);
    }

    void erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows,
                    bool broken_reciprocal_backlinks) override {
        m_seconds.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, broken_reciprocal_backlinks);
        m_nanoseconds.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, broken_reciprocal_backlinks);
    }

    void move_last_row_over(size_t row_ndx, size_t prior_num_rows,
                            bool broken_reciprocal_backlinks) override {
        m_seconds.move_last_row_over(row_ndx, prior_num_rows, broken_reciprocal_backlinks);
        m_nanoseconds.move_last_row_over(row_ndx, prior_num_rows, broken_reciprocal_backlinks);
    }

    void clear(size_t num_rows, bool broken_reciprocal_backlinks) override {
        m_seconds.clear(num_rows, broken_reciprocal_backlinks);
        m_nanoseconds.clear(num_rows, broken_reciprocal_backlinks);
    }

    void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override {
        m_seconds.swap_rows(row_ndx_1, row_ndx_2);
        m_nanoseconds.swap_rows(row_ndx_1, row_ndx_2);
    }

    void destroy() noexcept override {
        m_seconds.destroy();
        m_nanoseconds.destroy();
    }

    StringData get_index_data(size_t, StringIndex::StringConversionBuffer& buffer) const noexcept override {
        // FIXME: Dummy implementation
        return null();
    }

    Allocator& get_alloc() const noexcept override {
        // FIXME: Dummy implementation
        return Allocator::get_default();
    }

    /// Returns the 'ref' of the root array.
    ref_type get_ref() const noexcept override {
        // FIXME: Dummy implementation
        return 0;
    }
    MemRef get_mem() const noexcept override {
        // FIXME: Dummy implementation
        return MemRef();
    }

    void replace_root_array(std::unique_ptr<Array> leaf) override {
        // FIXME: Dummy implementation

    }

    MemRef clone_deep(Allocator& alloc) const override {
        // FIXME: Dummy implementation
        return MemRef();

    }

    void detach() override {
        m_seconds.detach();
        m_nanoseconds.detach();
    }

    bool is_attached() const noexcept override {
        // FIXME: Assert on both columns having same attached state?
        return m_seconds.is_attached();
    }

    ref_type write(size_t slice_offset, size_t slice_size,
                   size_t table_size, _impl::OutputStream&) const override {
        // FIXME: Dummy implementation
        return 0;
    }

    void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept override {
        // FIXME: Dummy implementation

    }

    size_t get_ndx_in_parent() const noexcept override {
        // FIXME: Dummy implementation
        return 0;
    }

    void set_ndx_in_parent(size_t ndx_in_parent) noexcept override {
        // FIXME: Dummy implementation
    }

    void update_from_parent(size_t old_baseline) noexcept override {
        // FIXME: Dummy implementation
    }

    void refresh_accessor_tree(size_t new_col_ndx, const Spec&) override {
        // FIXME: Dummy implementation
    }

    void verify() const override {
        // FIXME: Dummy implementation
    }

    void to_dot(std::ostream&, StringData title = StringData()) const override {
        // FIXME: Dummy implementation
    }

    void do_dump_node_structure(std::ostream&, int level) const override {
        // FIXME: Dummy implementation
    }

    void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent,
                     std::ostream&) const override {
        // FIXME: Dummy implementation
    }

    void add(const NewDate& ndt = NewDate{}) {
        util::Optional<int64_t> seconds = ndt.is_null() ? util::none : util::make_optional(ndt.m_seconds);
        int32_t nanoseconds = ndt.is_null() ? 0 : ndt.m_nanoseconds;
        m_seconds.add(seconds);
        m_nanoseconds.add(nanoseconds);
    }

    NewDate get(size_t row_ndx) const noexcept {
        util::Optional<int64_t> seconds = m_seconds.get(row_ndx);
        return seconds ? NewDate(*seconds, m_nanoseconds.get(row_ndx)) : NewDate();
    }

private:
    IntNullColumn m_seconds;
    IntegerColumn m_nanoseconds;
};

} // namespace realm

#endif // REALM_COLUMN_DATETIME_HPP
<commit_msg>Fixed copy-paste error<commit_after>/*************************************************************************
 *
 * REALM CONFIDENTIAL
 * __________________
 *
 *  [2011] - [2015] Realm Inc
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Realm Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Realm Incorporated
 * and its suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Realm Incorporated.
 *
 **************************************************************************/
#ifndef REALM_COLUMN_DATETIME_HPP
#define REALM_COLUMN_DATETIME_HPP

#include <realm/column.hpp>

namespace realm {

struct NewDate {
    NewDate(int64_t seconds, int32_t nanoseconds) : m_seconds(seconds), m_nanoseconds(nanoseconds), m_is_null(false) { }
    NewDate(const null&) : m_is_null(true) { }
    NewDate() : NewDate(null()) { }

    bool is_null() const { return m_is_null; }
    bool operator == (const NewDate& rhs) const { return m_seconds == rhs.m_seconds && m_nanoseconds == rhs.m_nanoseconds; }
    bool operator != (const NewDate& rhs) const { return m_seconds != rhs.m_seconds || m_nanoseconds != rhs.m_nanoseconds; }
    bool operator > (const NewDate& rhs) const { return (m_seconds > rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds > rhs.m_nanoseconds); }
    bool operator < (const NewDate& rhs) const { return (m_seconds < rhs.m_seconds) || (m_seconds == rhs.m_seconds && m_nanoseconds < rhs.m_nanoseconds); }
    NewDate& operator = (const NewDate& rhs) = default;

    int64_t m_seconds;
    int32_t m_nanoseconds;
    bool m_is_null;
};

class DateTimeColumn : public ColumnBase {
public:
    DateTimeColumn() : m_seconds(Allocator::get_default(), IntNullColumn::create(Allocator::get_default())),
                       m_nanoseconds(Allocator::get_default(), IntegerColumn::create(Allocator::get_default()))
    {
    }

    /// Get the number of entries in this column. This operation is relatively
    /// slow.
    size_t size() const noexcept override {
        // FIXME: Consider debug asserts on the columns having the same size
        return m_seconds.size();
    }

    /// Whether or not this column is nullable.
    bool is_nullable() const noexcept override {
        return m_seconds.is_nullable();
    }

    /// Whether or not the value at \a row_ndx is NULL. If the column is not
    /// nullable, always returns false.
    bool is_null(size_t row_ndx) const noexcept override {
        return m_seconds.is_null(row_ndx);
    }

    /// Sets the value at \a row_ndx to be NULL.
    /// \throw LogicError Thrown if this column is not nullable.
    void set_null(size_t row_ndx) override {
        m_seconds.set_null(row_ndx);
    }

    void insert_rows(size_t row_ndx, size_t num_rows_to_insert, size_t prior_num_rows, bool nullable) override {
        m_seconds.insert_rows(row_ndx, num_rows_to_insert, prior_num_rows, nullable);
        m_nanoseconds.insert_rows(row_ndx, num_rows_to_insert, prior_num_rows, nullable);
    }

    void erase_rows(size_t row_ndx, size_t num_rows_to_erase, size_t prior_num_rows,
                    bool broken_reciprocal_backlinks) override {
        m_seconds.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, broken_reciprocal_backlinks);
        m_nanoseconds.erase_rows(row_ndx, num_rows_to_erase, prior_num_rows, broken_reciprocal_backlinks);
    }

    void move_last_row_over(size_t row_ndx, size_t prior_num_rows,
                            bool broken_reciprocal_backlinks) override {
        m_seconds.move_last_row_over(row_ndx, prior_num_rows, broken_reciprocal_backlinks);
        m_nanoseconds.move_last_row_over(row_ndx, prior_num_rows, broken_reciprocal_backlinks);
    }

    void clear(size_t num_rows, bool broken_reciprocal_backlinks) override {
        m_seconds.clear(num_rows, broken_reciprocal_backlinks);
        m_nanoseconds.clear(num_rows, broken_reciprocal_backlinks);
    }

    void swap_rows(size_t row_ndx_1, size_t row_ndx_2) override {
        m_seconds.swap_rows(row_ndx_1, row_ndx_2);
        m_nanoseconds.swap_rows(row_ndx_1, row_ndx_2);
    }

    void destroy() noexcept override {
        m_seconds.destroy();
        m_nanoseconds.destroy();
    }

    StringData get_index_data(size_t, StringIndex::StringConversionBuffer& buffer) const noexcept override {
        // FIXME: Dummy implementation
        return null();
    }

    Allocator& get_alloc() const noexcept override {
        // FIXME: Dummy implementation
        return Allocator::get_default();
    }

    /// Returns the 'ref' of the root array.
    ref_type get_ref() const noexcept override {
        // FIXME: Dummy implementation
        return 0;
    }
    MemRef get_mem() const noexcept override {
        // FIXME: Dummy implementation
        return MemRef();
    }

    void replace_root_array(std::unique_ptr<Array> leaf) override {
        // FIXME: Dummy implementation

    }

    MemRef clone_deep(Allocator& alloc) const override {
        // FIXME: Dummy implementation
        return MemRef();

    }

    void detach() override {
        m_seconds.detach();
        m_nanoseconds.detach();
    }

    bool is_attached() const noexcept override {
        // FIXME: Assert on both columns having same attached state?
        return m_seconds.is_attached();
    }

    ref_type write(size_t slice_offset, size_t slice_size,
                   size_t table_size, _impl::OutputStream&) const override {
        // FIXME: Dummy implementation
        return 0;
    }

    void set_parent(ArrayParent*, size_t ndx_in_parent) noexcept override {
        // FIXME: Dummy implementation

    }

    size_t get_ndx_in_parent() const noexcept override {
        // FIXME: Dummy implementation
        return 0;
    }

    void set_ndx_in_parent(size_t ndx_in_parent) noexcept override {
        // FIXME: Dummy implementation
    }

    void update_from_parent(size_t old_baseline) noexcept override {
        // FIXME: Dummy implementation
    }

    void refresh_accessor_tree(size_t new_col_ndx, const Spec&) override {
        // FIXME: Dummy implementation
    }

    void verify() const override {
        // FIXME: Dummy implementation
    }

    void to_dot(std::ostream&, StringData title = StringData()) const override {
        // FIXME: Dummy implementation
    }

    void do_dump_node_structure(std::ostream&, int level) const override {
        // FIXME: Dummy implementation
    }

    void leaf_to_dot(MemRef, ArrayParent*, size_t ndx_in_parent,
                     std::ostream&) const override {
        // FIXME: Dummy implementation
    }

    void add(const NewDate& ndt = NewDate{}) {
        util::Optional<int64_t> seconds = ndt.is_null() ? util::none : util::make_optional(ndt.m_seconds);
        int32_t nanoseconds = ndt.is_null() ? 0 : ndt.m_nanoseconds;
        m_seconds.add(seconds);
        m_nanoseconds.add(nanoseconds);
    }

    NewDate get(size_t row_ndx) const noexcept {
        util::Optional<int64_t> seconds = m_seconds.get(row_ndx);
        return seconds ? NewDate(*seconds, m_nanoseconds.get(row_ndx)) : NewDate();
    }

private:
    IntNullColumn m_seconds;
    IntegerColumn m_nanoseconds;
};

} // namespace realm

#endif // REALM_COLUMN_DATETIME_HPP
<|endoftext|>


<commit_before><commit_msg>Don't try to set transparency on pixbufs that don't have any transparency channel.<commit_after><|endoftext|>


<commit_before>/* Copyright (c) 2013-2014 the Civetweb developers
 * Copyright (c) 2013 No Face Press, LLC
 *
 * License http://opensource.org/licenses/mit-license.php MIT License
 */

#include "CivetServer.h"

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#ifndef UNUSED_PARAMETER
#define UNUSED_PARAMETER(x) (void)(x)
#endif

bool CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn)
{
    UNUSED_PARAMETER(server);
    UNUSED_PARAMETER(conn);
    return false;
}

bool CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn)
{
    UNUSED_PARAMETER(server);
    UNUSED_PARAMETER(conn);
    return false;
}

bool CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn)
{
    UNUSED_PARAMETER(server);
    UNUSED_PARAMETER(conn);
    return false;
}

bool CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn)
{
    UNUSED_PARAMETER(server);
    UNUSED_PARAMETER(conn);
    return false;
}

int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
{
    struct mg_request_info *request_info = mg_get_request_info(conn);
    assert(request_info != NULL);
    CivetServer *me = (CivetServer*) (request_info->user_data);
    assert(me != NULL);

    CivetHandler *handler = (CivetHandler *)cbdata;

    if (handler) {
        if (strcmp(request_info->request_method, "GET") == 0) {
            return handler->handleGet(me, conn) ? 1 : 0;
        } else if (strcmp(request_info->request_method, "POST") == 0) {
            return handler->handlePost(me, conn) ? 1 : 0;
        } else if (strcmp(request_info->request_method, "PUT") == 0) {
            return handler->handlePut(me, conn) ? 1 : 0;
        } else if (strcmp(request_info->request_method, "DELETE") == 0) {
            return handler->handleDelete(me, conn) ? 1 : 0;
        }
    }

    return 0; // No handler found
}

CivetServer::CivetServer(const char **options,
                         const struct mg_callbacks *_callbacks) :
    context(0), postData(0)
{
    struct mg_callbacks callbacks;
    memset(&callbacks, 0, sizeof(callbacks));
    if (_callbacks) {
        callbacks = *_callbacks;
        userCloseHandler = _callbacks->connection_close;
    } else {
        userCloseHandler = NULL;
    }
    callbacks.connection_close = closeHandler;

    context = mg_start(&callbacks, this, options);
}

CivetServer::~CivetServer()
{
    close();
}

void CivetServer::closeHandler(struct mg_connection *conn)
{
    struct mg_request_info *request_info = mg_get_request_info(conn);
    assert(request_info != NULL);
    CivetServer *me = (CivetServer*) (request_info->user_data);
    assert(me != NULL);

    if (me->userCloseHandler) me->userCloseHandler(conn);
    if (me->postData) {
        delete [] (me->postData);
        me->postData = 0;
    }
}

void CivetServer::addHandler(const std::string &uri, CivetHandler *handler)
{
    mg_set_request_handler(context, uri.c_str(), requestHandler, handler);
}

void CivetServer::removeHandler(const std::string &uri)
{
    mg_set_request_handler(context, uri.c_str(), NULL, NULL);
}

void CivetServer::close()
{
    if (context) {
        mg_stop (context);
        context = 0;
    }
}

int CivetServer::getCookie(struct mg_connection *conn, const std::string &cookieName, std::string &cookieValue)
{
    //Maximum cookie length as per microsoft is 4096. http://msdn.microsoft.com/en-us/library/ms178194.aspx
    char _cookieValue[4096];
    const char *cookie = mg_get_header(conn, "Cookie");
    int lRead = mg_get_cookie(cookie, cookieName.c_str(), _cookieValue, sizeof(_cookieValue));
    cookieValue.clear();
    cookieValue.append(_cookieValue);
    return lRead;
}

const char* CivetServer::getHeader(struct mg_connection *conn, const std::string &headerName)
{
    return mg_get_header(conn, headerName.c_str());
}

void
CivetServer::urlDecode(const char *src, std::string &dst, bool is_form_url_encoded)
{
    urlDecode(src, strlen(src), dst, is_form_url_encoded);
}

void
CivetServer::urlDecode(const char *src, size_t src_len, std::string &dst, bool is_form_url_encoded)
{
    int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')

    dst.clear();
    for (i = j = 0; i < (int)src_len; i++, j++) {
        if (src[i] == '%' && i < (int)src_len - 2 &&
            isxdigit(* (const unsigned char *) (src + i + 1)) &&
            isxdigit(* (const unsigned char *) (src + i + 2))) {
            a = tolower(* (const unsigned char *) (src + i + 1));
            b = tolower(* (const unsigned char *) (src + i + 2));
            dst.push_back((char) ((HEXTOI(a) << 4) | HEXTOI(b)));
            i += 2;
        } else if (is_form_url_encoded && src[i] == '+') {
            dst.push_back(' ');
        } else {
            dst.push_back(src[i]);
        }
    }
}

bool
CivetServer::getParam(struct mg_connection *conn, const char *name,
                      std::string &dst, size_t occurrence)
{
    const char *formParams = NULL;
    struct mg_request_info *ri = mg_get_request_info(conn);
    assert(ri != NULL);
    CivetServer *me = (CivetServer*) (ri->user_data);
    assert(me != NULL);

    if (me->postData != NULL) {
        formParams = me->postData;
    } else {
        const char * con_len_str = mg_get_header(conn, "Content-Length");
        if (con_len_str) {
            unsigned long con_len = atoi(con_len_str);
            if (con_len>0) {
                me->postData = new char[con_len];
                if (me->postData != NULL) {
                    /* NULL check is not required according to current C++ standard */
                    mg_read(conn, me->postData, con_len);
                    formParams = me->postData;
                }
            }
        }
    }
    if (formParams == NULL) {
        // get requests do store html <form> field values in the http query_string
        formParams = ri->query_string;
    }

    if (formParams != NULL) {
        return getParam(formParams, strlen(formParams), name, dst, occurrence);
    }

    return false;
}

bool
CivetServer::getParam(const char *data, size_t data_len, const char *name,
                      std::string &dst, size_t occurrence)
{
    const char *p, *e, *s;
    size_t name_len;

    dst.clear();
    if (data == NULL || name == NULL || data_len == 0) {
        return false;
    }
    name_len = strlen(name);
    e = data + data_len;

    // data is "var1=val1&var2=val2...". Find variable first
    for (p = data; p + name_len < e; p++) {
        if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
            !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {

            // Point p to variable value
            p += name_len + 1;

            // Point s to the end of the value
            s = (const char *) memchr(p, '&', (size_t)(e - p));
            if (s == NULL) {
                s = e;
            }
            assert(s >= p);

            // Decode variable into destination buffer
            urlDecode(p, (int)(s - p), dst, true);
            return true;
        }
    }
    return false;
}

void
CivetServer::urlEncode(const char *src, std::string &dst, bool append)
{
    urlEncode(src, strlen(src), dst, append);
}

void
CivetServer::urlEncode(const char *src, size_t src_len, std::string &dst, bool append)
{
    static const char *dont_escape = "._-$,;~()";
    static const char *hex = "0123456789abcdef";

    if (!append)
        dst.clear();

    for (; src_len > 0; src++, src_len--) {
        if (isalnum(*(const unsigned char *) src) ||
            strchr(dont_escape, * (const unsigned char *) src) != NULL) {
            dst.push_back(*src);
        } else {
            dst.push_back('%');
            dst.push_back(hex[(* (const unsigned char *) src) >> 4]);
            dst.push_back(hex[(* (const unsigned char *) src) & 0xf]);
        }
    }
}
<commit_msg>Use C memory allocation instead of C++, so std::bad_alloc exception handling must not be done<commit_after>/* Copyright (c) 2013-2014 the Civetweb developers
 * Copyright (c) 2013 No Face Press, LLC
 *
 * License http://opensource.org/licenses/mit-license.php MIT License
 */

#include "CivetServer.h"

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#ifndef UNUSED_PARAMETER
#define UNUSED_PARAMETER(x) (void)(x)
#endif

bool CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn)
{
    UNUSED_PARAMETER(server);
    UNUSED_PARAMETER(conn);
    return false;
}

bool CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn)
{
    UNUSED_PARAMETER(server);
    UNUSED_PARAMETER(conn);
    return false;
}

bool CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn)
{
    UNUSED_PARAMETER(server);
    UNUSED_PARAMETER(conn);
    return false;
}

bool CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn)
{
    UNUSED_PARAMETER(server);
    UNUSED_PARAMETER(conn);
    return false;
}

int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
{
    struct mg_request_info *request_info = mg_get_request_info(conn);
    assert(request_info != NULL);
    CivetServer *me = (CivetServer*) (request_info->user_data);
    assert(me != NULL);

    CivetHandler *handler = (CivetHandler *)cbdata;

    if (handler) {
        if (strcmp(request_info->request_method, "GET") == 0) {
            return handler->handleGet(me, conn) ? 1 : 0;
        } else if (strcmp(request_info->request_method, "POST") == 0) {
            return handler->handlePost(me, conn) ? 1 : 0;
        } else if (strcmp(request_info->request_method, "PUT") == 0) {
            return handler->handlePut(me, conn) ? 1 : 0;
        } else if (strcmp(request_info->request_method, "DELETE") == 0) {
            return handler->handleDelete(me, conn) ? 1 : 0;
        }
    }

    return 0; // No handler found
}

CivetServer::CivetServer(const char **options,
                         const struct mg_callbacks *_callbacks) :
    context(0), postData(0)
{
    struct mg_callbacks callbacks;
    memset(&callbacks, 0, sizeof(callbacks));
    if (_callbacks) {
        callbacks = *_callbacks;
        userCloseHandler = _callbacks->connection_close;
    } else {
        userCloseHandler = NULL;
    }
    callbacks.connection_close = closeHandler;

    context = mg_start(&callbacks, this, options);
}

CivetServer::~CivetServer()
{
    close();
}

void CivetServer::closeHandler(struct mg_connection *conn)
{
    struct mg_request_info *request_info = mg_get_request_info(conn);
    assert(request_info != NULL);
    CivetServer *me = (CivetServer*) (request_info->user_data);
    assert(me != NULL);

    if (me->userCloseHandler) me->userCloseHandler(conn);
    if (me->postData) {
        free(me->postData);
        me->postData = 0;
    }
}

void CivetServer::addHandler(const std::string &uri, CivetHandler *handler)
{
    mg_set_request_handler(context, uri.c_str(), requestHandler, handler);
}

void CivetServer::removeHandler(const std::string &uri)
{
    mg_set_request_handler(context, uri.c_str(), NULL, NULL);
}

void CivetServer::close()
{
    if (context) {
        mg_stop (context);
        context = 0;
    }
}

int CivetServer::getCookie(struct mg_connection *conn, const std::string &cookieName, std::string &cookieValue)
{
    //Maximum cookie length as per microsoft is 4096. http://msdn.microsoft.com/en-us/library/ms178194.aspx
    char _cookieValue[4096];
    const char *cookie = mg_get_header(conn, "Cookie");
    int lRead = mg_get_cookie(cookie, cookieName.c_str(), _cookieValue, sizeof(_cookieValue));
    cookieValue.clear();
    cookieValue.append(_cookieValue);
    return lRead;
}

const char* CivetServer::getHeader(struct mg_connection *conn, const std::string &headerName)
{
    return mg_get_header(conn, headerName.c_str());
}

void
CivetServer::urlDecode(const char *src, std::string &dst, bool is_form_url_encoded)
{
    urlDecode(src, strlen(src), dst, is_form_url_encoded);
}

void
CivetServer::urlDecode(const char *src, size_t src_len, std::string &dst, bool is_form_url_encoded)
{
    int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')

    dst.clear();
    for (i = j = 0; i < (int)src_len; i++, j++) {
        if (src[i] == '%' && i < (int)src_len - 2 &&
            isxdigit(* (const unsigned char *) (src + i + 1)) &&
            isxdigit(* (const unsigned char *) (src + i + 2))) {
            a = tolower(* (const unsigned char *) (src + i + 1));
            b = tolower(* (const unsigned char *) (src + i + 2));
            dst.push_back((char) ((HEXTOI(a) << 4) | HEXTOI(b)));
            i += 2;
        } else if (is_form_url_encoded && src[i] == '+') {
            dst.push_back(' ');
        } else {
            dst.push_back(src[i]);
        }
    }
}

bool
CivetServer::getParam(struct mg_connection *conn, const char *name,
                      std::string &dst, size_t occurrence)
{
    const char *formParams = NULL;
    struct mg_request_info *ri = mg_get_request_info(conn);
    assert(ri != NULL);
    CivetServer *me = (CivetServer*) (ri->user_data);
    assert(me != NULL);

    if (me->postData != NULL) {
        formParams = me->postData;
    } else {
        const char * con_len_str = mg_get_header(conn, "Content-Length");
        if (con_len_str) {
            unsigned long con_len = atoi(con_len_str);
            if (con_len>0) {
                me->postData = (char*)malloc(con_len);
                if (me->postData != NULL) {
                    /* malloc may fail for huge requests */
                    mg_read(conn, me->postData, con_len);
                    formParams = me->postData;
                }
            }
        }
    }
    if (formParams == NULL) {
        // get requests do store html <form> field values in the http query_string
        formParams = ri->query_string;
    }

    if (formParams != NULL) {
        return getParam(formParams, strlen(formParams), name, dst, occurrence);
    }

    return false;
}

bool
CivetServer::getParam(const char *data, size_t data_len, const char *name,
                      std::string &dst, size_t occurrence)
{
    const char *p, *e, *s;
    size_t name_len;

    dst.clear();
    if (data == NULL || name == NULL || data_len == 0) {
        return false;
    }
    name_len = strlen(name);
    e = data + data_len;

    // data is "var1=val1&var2=val2...". Find variable first
    for (p = data; p + name_len < e; p++) {
        if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
            !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {

            // Point p to variable value
            p += name_len + 1;

            // Point s to the end of the value
            s = (const char *) memchr(p, '&', (size_t)(e - p));
            if (s == NULL) {
                s = e;
            }
            assert(s >= p);

            // Decode variable into destination buffer
            urlDecode(p, (int)(s - p), dst, true);
            return true;
        }
    }
    return false;
}

void
CivetServer::urlEncode(const char *src, std::string &dst, bool append)
{
    urlEncode(src, strlen(src), dst, append);
}

void
CivetServer::urlEncode(const char *src, size_t src_len, std::string &dst, bool append)
{
    static const char *dont_escape = "._-$,;~()";
    static const char *hex = "0123456789abcdef";

    if (!append)
        dst.clear();

    for (; src_len > 0; src++, src_len--) {
        if (isalnum(*(const unsigned char *) src) ||
            strchr(dont_escape, * (const unsigned char *) src) != NULL) {
            dst.push_back(*src);
        } else {
            dst.push_back('%');
            dst.push_back(hex[(* (const unsigned char *) src) >> 4]);
            dst.push_back(hex[(* (const unsigned char *) src) & 0xf]);
        }
    }
}
<|endoftext|>


<commit_before>#include "errors.hpp"
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/make_shared.hpp>

#include "concurrency/pmap.hpp"
#include "concurrency/wait_any.hpp"

template<class metadata_t>
metadata_cluster_t<metadata_t>::metadata_cluster_t(int port, const metadata_t &initial_metadata) :
    mailbox_cluster_t(port),
    /* Watch ourself for new peers connecting */
    event_watcher_t(this),
    root_view(boost::make_shared<root_view_t>(this)),
    metadata(initial_metadata),
    change_publisher(&change_mutex),
    ping_id_counter(0)
    { }

template<class metadata_t>
metadata_cluster_t<metadata_t>::~metadata_cluster_t() {
    root_view->parent = NULL;
}

template<class metadata_t>
boost::shared_ptr<metadata_readwrite_view_t<metadata_t> > metadata_cluster_t<metadata_t>::get_root_view() {
    assert_thread();
    return root_view;
}

template<class metadata_t>
metadata_cluster_t<metadata_t>::root_view_t::root_view_t(metadata_cluster_t *p) :
    parent(p) { }

template<class metadata_t>
metadata_t metadata_cluster_t<metadata_t>::root_view_t::get() {
    rassert(parent, "accessing `metadata_cluster_t` root view when cluster no "
        "longer exists");
    parent->assert_thread();
    return parent->metadata;
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::root_view_t::join(const metadata_t &added_metadata) {
    rassert(parent, "accessing `metadata_cluster_t` root view when cluster no "
        "longer exists");
    parent->assert_thread();

    parent->join_metadata_locally(added_metadata);

    /* Distribute changes to all peers we can currently see. If we can't
    currently see a peer, that's OK; it will hear about the metadata change when
    it reconnects, via the `metadata_cluster_t`'s `on_connect()` handler. */
    std::map<peer_id_t, peer_address_t> peers = parent->get_everybody();
    for (std::map<peer_id_t, peer_address_t>::iterator it = peers.begin(); it != peers.end(); it++) {
        peer_id_t peer = (*it).first;
        if (peer != parent->get_me()) {
            parent->send_utility_message(peer,
                boost::bind(&metadata_cluster_t<metadata_t>::write_metadata, _1, added_metadata));
        }
    }
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::root_view_t::sync_from(peer_id_t peer, signal_t *interruptor) THROWS_ONLY(interrupted_exc_t, sync_failed_exc_t) {
    rassert(parent, "accessing `metadata_cluster_t` root view when cluster no "
        "longer exists");
    parent->assert_thread();
    int ping_id = parent->ping_id_counter++;
    cond_t response_cond;
    map_insertion_sentry_t<int, cond_t *> response_listener(&parent->ping_waiters, ping_id, &response_cond);
    disconnect_watcher_t watcher(parent, peer);
    parent->send_utility_message(peer,
        boost::bind(&metadata_cluster_t<metadata_t>::write_ping, _1, ping_id));
    wait_any_t waiter(&response_cond, &watcher, interruptor);
    waiter.wait_lazily_unordered();
    if (interruptor->is_pulsed()) {
        throw interrupted_exc_t();
    }
    if (watcher.is_pulsed()) {
        throw sync_failed_exc_t();
    }
    rassert(response_cond.is_pulsed());
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::root_view_t::sync_to(peer_id_t peer, signal_t *interruptor) THROWS_ONLY(interrupted_exc_t, sync_failed_exc_t) {
    /* For now we just implement `sync_to()` the same way we implement
    `sync_from()`: we ping the peer. In the future, it could send an ack every
    time the metadata changes and we could just check how recently the last ack
    was. */
    sync_from(peer, interruptor);
}

template<class metadata_t>
publisher_t<boost::function<void()> > *metadata_cluster_t<metadata_t>::root_view_t::get_publisher() {
    rassert(parent, "accessing `metadata_cluster_t` root view when cluster no "
        "longer exists");
    parent->assert_thread();
    return parent->change_publisher.get_publisher();
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::join_metadata_locally(metadata_t added_metadata) {
    assert_thread();
    mutex_acquisition_t change_acq(&change_mutex);
    semilattice_join(&metadata, added_metadata);
    change_publisher.publish(&metadata_cluster_t<metadata_t>::call, &change_acq);
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::call(boost::function<void()> fun) {
    fun();
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::write_metadata(std::ostream &stream, metadata_t md) {
    stream << 'M';
    boost::archive::binary_oarchive archive(stream);
    archive << md;
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::write_ping(std::ostream &stream, int ping_id) {
    stream << 'P';
    stream.write(reinterpret_cast<const char *>(&ping_id), sizeof(ping_id));
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::write_ping_response(std::ostream &stream, int ping_id) {
    stream << 'R';
    stream.write(reinterpret_cast<const char *>(&ping_id), sizeof(ping_id));
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::on_utility_message(peer_id_t sender, std::istream &stream, boost::function<void()> &on_done) {
    assert_thread();
    char code;
    stream >> code;
    // TODO: Hard-coded constants.
    if (code == 'M') {
        metadata_t added_metadata;
        {
            boost::archive::binary_iarchive archive(stream);
            archive >> added_metadata;
        }
        on_done();
        join_metadata_locally(added_metadata);
    } else if (code == 'P') {
        int ping_id;
        stream.read(reinterpret_cast<char *>(&ping_id), sizeof(ping_id));
        on_done();
        send_utility_message(sender,
            boost::bind(&metadata_cluster_t<metadata_t>::write_ping_response, _1, ping_id));
    } else if (code == 'R') {
        int ping_id;
        stream.read(reinterpret_cast<char *>(&ping_id), sizeof(ping_id));
        on_done();
        std::map<int, cond_t *>::iterator it = ping_waiters.find(ping_id);
        if (it != ping_waiters.end()) {
            (*it).second->pulse();
        }
    } else {
        // TODO: Crashing is debatable.
        crash("Unexpected utility message code: %d", (int)code);
    }
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::on_connect(peer_id_t peer) {
    assert_thread();
    /* We have to spawn this in a separate coroutine because `on_connect()` is
    not supposed to block. */
    coro_t::spawn_now(boost::bind(
        &mailbox_cluster_t::send_utility_message,
        this,
        peer,
        boost::function<void(std::ostream&)>(
            boost::bind(&metadata_cluster_t<metadata_t>::write_metadata, _1, metadata)
            )
        ));
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::on_disconnect(peer_id_t) {
    /* Ignore event */
}

<commit_msg>Added some TODO THREAD comments in metadata.tcc.<commit_after>#include "errors.hpp"
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/make_shared.hpp>

#include "concurrency/pmap.hpp"
#include "concurrency/wait_any.hpp"

template<class metadata_t>
metadata_cluster_t<metadata_t>::metadata_cluster_t(int port, const metadata_t &initial_metadata) :
    mailbox_cluster_t(port),
    /* Watch ourself for new peers connecting */
    event_watcher_t(this),
    root_view(boost::make_shared<root_view_t>(this)),
    metadata(initial_metadata),
    change_publisher(&change_mutex),
    ping_id_counter(0)
    { }

template<class metadata_t>
metadata_cluster_t<metadata_t>::~metadata_cluster_t() {
    // TODO THREAD assert thread
    root_view->parent = NULL;
}

template<class metadata_t>
boost::shared_ptr<metadata_readwrite_view_t<metadata_t> > metadata_cluster_t<metadata_t>::get_root_view() {
    assert_thread();
    return root_view;
}

template<class metadata_t>
metadata_cluster_t<metadata_t>::root_view_t::root_view_t(metadata_cluster_t *p) :
    parent(p) { /* TODO THREAD */ }

template<class metadata_t>
metadata_t metadata_cluster_t<metadata_t>::root_view_t::get() {
    // TODO THREAD
    rassert(parent, "accessing `metadata_cluster_t` root view when cluster no "
        "longer exists");
    parent->assert_thread();
    return parent->metadata;
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::root_view_t::join(const metadata_t &added_metadata) {
    // TODO THREAD gotta be home thread.
    rassert(parent, "accessing `metadata_cluster_t` root view when cluster no "
        "longer exists");
    parent->assert_thread();

    parent->join_metadata_locally(added_metadata);

    /* Distribute changes to all peers we can currently see. If we can't
    currently see a peer, that's OK; it will hear about the metadata change when
    it reconnects, via the `metadata_cluster_t`'s `on_connect()` handler. */
    std::map<peer_id_t, peer_address_t> peers = parent->get_everybody();
    for (std::map<peer_id_t, peer_address_t>::iterator it = peers.begin(); it != peers.end(); it++) {
        peer_id_t peer = (*it).first;
        if (peer != parent->get_me()) {
            parent->send_utility_message(peer,
                boost::bind(&metadata_cluster_t<metadata_t>::write_metadata, _1, added_metadata));
        }
    }
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::root_view_t::sync_from(peer_id_t peer, signal_t *interruptor) THROWS_ONLY(interrupted_exc_t, sync_failed_exc_t) {
    // TODO THREAD gotta be home thread.
    rassert(parent, "accessing `metadata_cluster_t` root view when cluster no "
        "longer exists");
    parent->assert_thread();
    int ping_id = parent->ping_id_counter++;
    cond_t response_cond;
    map_insertion_sentry_t<int, cond_t *> response_listener(&parent->ping_waiters, ping_id, &response_cond);
    disconnect_watcher_t watcher(parent, peer);
    parent->send_utility_message(peer,
        boost::bind(&metadata_cluster_t<metadata_t>::write_ping, _1, ping_id));
    wait_any_t waiter(&response_cond, &watcher, interruptor);
    waiter.wait_lazily_unordered();
    if (interruptor->is_pulsed()) {
        throw interrupted_exc_t();
    }
    if (watcher.is_pulsed()) {
        throw sync_failed_exc_t();
    }
    rassert(response_cond.is_pulsed());
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::root_view_t::sync_to(peer_id_t peer, signal_t *interruptor) THROWS_ONLY(interrupted_exc_t, sync_failed_exc_t) {
    // TODO THREAD what thread?
    /* For now we just implement `sync_to()` the same way we implement
    `sync_from()`: we ping the peer. In the future, it could send an ack every
    time the metadata changes and we could just check how recently the last ack
    was. */
    sync_from(peer, interruptor);
}

template<class metadata_t>
publisher_t<boost::function<void()> > *metadata_cluster_t<metadata_t>::root_view_t::get_publisher() {
    // TODO THREAD gotta be home thread.
    rassert(parent, "accessing `metadata_cluster_t` root view when cluster no "
        "longer exists");
    parent->assert_thread();
    return parent->change_publisher.get_publisher();
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::join_metadata_locally(metadata_t added_metadata) {
    // TODO THREAD gotta be home thread.
    assert_thread();
    mutex_acquisition_t change_acq(&change_mutex);
    semilattice_join(&metadata, added_metadata);
    change_publisher.publish(&metadata_cluster_t<metadata_t>::call, &change_acq);
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::call(boost::function<void()> fun) {
    // TODO THREAD wtf is this?
    fun();
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::write_metadata(std::ostream &stream, metadata_t md) {
    // THREAD connection thread.
    stream << 'M';
    boost::archive::binary_oarchive archive(stream);
    archive << md;
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::write_ping(std::ostream &stream, int ping_id) {
    // THREAD connection thread.
    stream << 'P';
    stream.write(reinterpret_cast<const char *>(&ping_id), sizeof(ping_id));
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::write_ping_response(std::ostream &stream, int ping_id) {
    // THREAD connection thread
    stream << 'R';
    stream.write(reinterpret_cast<const char *>(&ping_id), sizeof(ping_id));
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::on_utility_message(peer_id_t sender, std::istream &stream, boost::function<void()> &on_done) {
    // THREAD connection thread
    // TODO THREAD bad assertion here
    assert_thread();
    char code;
    stream >> code;
    // TODO: Hard-coded constants.
    if (code == 'M') {
        metadata_t added_metadata;
        {
            boost::archive::binary_iarchive archive(stream);
            archive >> added_metadata;
        }
        on_done();
        join_metadata_locally(added_metadata);
    } else if (code == 'P') {
        int ping_id;
        stream.read(reinterpret_cast<char *>(&ping_id), sizeof(ping_id));
        on_done();
        send_utility_message(sender,
            boost::bind(&metadata_cluster_t<metadata_t>::write_ping_response, _1, ping_id));
    } else if (code == 'R') {
        int ping_id;
        stream.read(reinterpret_cast<char *>(&ping_id), sizeof(ping_id));
        on_done();
        std::map<int, cond_t *>::iterator it = ping_waiters.find(ping_id);
        if (it != ping_waiters.end()) {
            (*it).second->pulse();
        }
    } else {
        // TODO: Crashing is debatable.
        crash("Unexpected utility message code: %d", (int)code);
    }
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::on_connect(peer_id_t peer) {
    // TODO THREAD connection thread?
    assert_thread();
    /* We have to spawn this in a separate coroutine because `on_connect()` is
    not supposed to block. */
    coro_t::spawn_now(boost::bind(
        &mailbox_cluster_t::send_utility_message,
        this,
        peer,
        boost::function<void(std::ostream&)>(
            boost::bind(&metadata_cluster_t<metadata_t>::write_metadata, _1, metadata)
            )
        ));
}

template<class metadata_t>
void metadata_cluster_t<metadata_t>::on_disconnect(peer_id_t) {
    // TODO THREAD assert thread
    /* Ignore event */
}

<|endoftext|>


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

#include "base/thread.h"
#include "chrome/browser/worker_host/worker_service.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_layout_test.h"

static const char kTestCompleteCookie[] = "status";
static const char kTestCompleteSuccess[] = "OK";

class WorkerTest : public UILayoutTest {
 protected:
  virtual ~WorkerTest() { }

  void RunTest(const std::wstring& test_case) {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    ASSERT_TRUE(tab.get());

    GURL url = GetTestUrl(L"workers", test_case);
    ASSERT_TRUE(tab->NavigateToURL(url));

    std::string value = WaitUntilCookieNonEmpty(tab.get(), url,
        kTestCompleteCookie, kTestIntervalMs, kTestWaitTimeoutMs);
    ASSERT_STREQ(kTestCompleteSuccess, value.c_str());
  }
};

TEST_F(WorkerTest, SingleWorker) {
  RunTest(L"single_worker.html");
}

TEST_F(WorkerTest, MultipleWorkers) {
  RunTest(L"multi_worker.html");
}

// WorkerFastLayoutTests works on the linux try servers, but fails on the
// build bots and fails on mac valgrind.
#if !defined(OS_WIN)
#define WorkerFastLayoutTests DISABLED_WorkerFastLayoutTests
#endif

// WorkerFastLayoutTests failed on all platforms since r27553.
// http://crbug.com/23391
TEST_F(WorkerTest, DISABLED_WorkerFastLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    "stress-js-execution.html",
#if defined(OS_WIN)
    // Workers don't properly initialize the V8 stack guard.
    // (http://code.google.com/p/chromium/issues/detail?id=21653).
    "use-machine-stack.html",
#endif
    "worker-call.html",
    // Disabled because cloning ports are too slow in Chromium to meet the
    // thresholds in this test.
    // http://code.google.com/p/chromium/issues/detail?id=22780
    // "worker-cloneport.html",

    "worker-close.html",
    "worker-constructor.html",
    "worker-context-gc.html",
    "worker-context-multi-port.html",
    "worker-event-listener.html",
    "worker-gc.html",
    // worker-lifecycle.html relies on layoutTestController.workerThreadCount
    // which is not currently implemented.
    // "worker-lifecycle.html",
    "worker-location.html",
    "worker-messageport.html",
    // Disabled after r27089 (WebKit merge), http://crbug.com/22947
    // "worker-messageport-gc.html",
    "worker-multi-port.html",
    "worker-navigator.html",
    "worker-replace-global-constructor.html",
    "worker-replace-self.html",
    "worker-script-error.html",
    "worker-terminate.html",
    "worker-timeout.html"
  };

  FilePath fast_test_dir;
  fast_test_dir = fast_test_dir.AppendASCII("LayoutTests");
  fast_test_dir = fast_test_dir.AppendASCII("fast");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(fast_test_dir, worker_test_dir, false);

  // Worker tests also rely on common files in js/resources.
  FilePath js_dir = fast_test_dir.AppendASCII("js");
  FilePath resource_dir;
  resource_dir = resource_dir.AppendASCII("resources");
  AddResourceForLayoutTest(js_dir, resource_dir);

  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], false);
}

TEST_F(WorkerTest, WorkerHttpLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    // flakey? BUG 16934 "text-encoding.html",
#if defined(OS_WIN)
    // Fails on the mac (and linux?):
    // http://code.google.com/p/chromium/issues/detail?id=22599
    "worker-importScripts.html",
#endif
    "worker-redirect.html",
  };

  FilePath http_test_dir;
  http_test_dir = http_test_dir.AppendASCII("LayoutTests");
  http_test_dir = http_test_dir.AppendASCII("http");
  http_test_dir = http_test_dir.AppendASCII("tests");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(http_test_dir, worker_test_dir, true);

  StartHttpServer(new_http_root_dir_);
  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], true);
  StopHttpServer();
}

TEST_F(WorkerTest, WorkerXhrHttpLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    "abort-exception-assert.html",
#if defined(OS_WIN)
    // Fails on the mac (and linux?):
    // http://code.google.com/p/chromium/issues/detail?id=22599
    "close.html",
#endif
    //"methods-async.html",
    //"methods.html",
    "xmlhttprequest-file-not-found.html"
  };

  FilePath http_test_dir;
  http_test_dir = http_test_dir.AppendASCII("LayoutTests");
  http_test_dir = http_test_dir.AppendASCII("http");
  http_test_dir = http_test_dir.AppendASCII("tests");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("xmlhttprequest");
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(http_test_dir, worker_test_dir, true);

  StartHttpServer(new_http_root_dir_);
  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], true);
  StopHttpServer();
}

TEST_F(WorkerTest, MessagePorts) {
  static const char* kLayoutTestFiles[] = {
    "message-channel-gc.html",
    "message-channel-gc-2.html",
    "message-channel-gc-3.html",
    "message-channel-gc-4.html",
    "message-port.html",
    "message-port-clone.html",
    // http://code.google.com/p/chromium/issues/detail?id=23709
    // "message-port-constructor-for-deleted-document.html",
    "message-port-deleted-document.html",
    "message-port-deleted-frame.html",
    // http://crbug.com/23597 (caused by http://trac.webkit.org/changeset/48978)
    // "message-port-inactive-document.html",
    "message-port-multi.html",
    "message-port-no-wrapper.html",
    // Only works with run-webkit-tests --leaks.
    //"message-channel-listener-circular-ownership.html",
  };

  FilePath fast_test_dir;
  fast_test_dir = fast_test_dir.AppendASCII("LayoutTests");
  fast_test_dir = fast_test_dir.AppendASCII("fast");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("events");
  InitializeForLayoutTest(fast_test_dir, worker_test_dir, false);

  // MessagePort tests also rely on common files in js/resources.
  FilePath js_dir = fast_test_dir.AppendASCII("js");
  FilePath resource_dir;
  resource_dir = resource_dir.AppendASCII("resources");
  AddResourceForLayoutTest(js_dir, resource_dir);

  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], false);
}

// Disable LimitPerPage on Linux. Seems to work on Mac though:
// http://code.google.com/p/chromium/issues/detail?id=22608
#if !defined(OS_LINUX)
TEST_F(WorkerTest, LimitPerPage) {
  int max_workers_per_tab = WorkerService::kMaxWorkersPerTabWhenSeparate;
  GURL url = GetTestUrl(L"workers", L"many_workers.html");
  url = GURL(url.spec() + StringPrintf("?count=%d", max_workers_per_tab + 1));

  scoped_refptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->NavigateToURL(url));

  EXPECT_EQ(max_workers_per_tab + 1 + (UITest::in_process_renderer() ? 0 : 1),
            UITest::GetBrowserProcessCount());
}
#endif

// http://code.google.com/p/chromium/issues/detail?id=22608
TEST_F(WorkerTest, DISABLED_LimitTotal) {
  int max_workers_per_tab = WorkerService::kMaxWorkersPerTabWhenSeparate;
  int total_workers = WorkerService::kMaxWorkersWhenSeparate;

  int tab_count = (total_workers / max_workers_per_tab) + 1;
  GURL url = GetTestUrl(L"workers", L"many_workers.html");
  url = GURL(url.spec() + StringPrintf("?count=%d", max_workers_per_tab));

  scoped_refptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->NavigateToURL(url));
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  for (int i = 1; i < tab_count; ++i)
    window->AppendTab(url);

  // Check that we didn't create more than the max number of workers.
  EXPECT_EQ(total_workers + 1 + (UITest::in_process_renderer() ? 0 : tab_count),
            UITest::GetBrowserProcessCount());

  // Now close the first tab and check that the queued workers were started.
  ASSERT_TRUE(tab->Close(true));
  // Give the tab process time to shut down.
  PlatformThread::Sleep(sleep_timeout_ms());

  EXPECT_EQ(total_workers + 1 + (UITest::in_process_renderer() ? 0 : tab_count),
            UITest::GetBrowserProcessCount());
}
<commit_msg>Enable WorkerTest.LimitTotal<commit_after>// Copyright (c) 2009 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/worker_host/worker_service.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_layout_test.h"

static const char kTestCompleteCookie[] = "status";
static const char kTestCompleteSuccess[] = "OK";

class WorkerTest : public UILayoutTest {
 protected:
  virtual ~WorkerTest() { }

  void RunTest(const std::wstring& test_case) {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    ASSERT_TRUE(tab.get());

    GURL url = GetTestUrl(L"workers", test_case);
    ASSERT_TRUE(tab->NavigateToURL(url));

    std::string value = WaitUntilCookieNonEmpty(tab.get(), url,
        kTestCompleteCookie, kTestIntervalMs, kTestWaitTimeoutMs);
    ASSERT_STREQ(kTestCompleteSuccess, value.c_str());
  }
};

TEST_F(WorkerTest, SingleWorker) {
  RunTest(L"single_worker.html");
}

TEST_F(WorkerTest, MultipleWorkers) {
  RunTest(L"multi_worker.html");
}

// WorkerFastLayoutTests works on the linux try servers, but fails on the
// build bots and fails on mac valgrind.
#if !defined(OS_WIN)
#define WorkerFastLayoutTests DISABLED_WorkerFastLayoutTests
#endif

// WorkerFastLayoutTests failed on all platforms since r27553.
// http://crbug.com/23391
TEST_F(WorkerTest, DISABLED_WorkerFastLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    "stress-js-execution.html",
#if defined(OS_WIN)
    // Workers don't properly initialize the V8 stack guard.
    // (http://code.google.com/p/chromium/issues/detail?id=21653).
    "use-machine-stack.html",
#endif
    "worker-call.html",
    // Disabled because cloning ports are too slow in Chromium to meet the
    // thresholds in this test.
    // http://code.google.com/p/chromium/issues/detail?id=22780
    // "worker-cloneport.html",

    "worker-close.html",
    "worker-constructor.html",
    "worker-context-gc.html",
    "worker-context-multi-port.html",
    "worker-event-listener.html",
    "worker-gc.html",
    // worker-lifecycle.html relies on layoutTestController.workerThreadCount
    // which is not currently implemented.
    // "worker-lifecycle.html",
    "worker-location.html",
    "worker-messageport.html",
    // Disabled after r27089 (WebKit merge), http://crbug.com/22947
    // "worker-messageport-gc.html",
    "worker-multi-port.html",
    "worker-navigator.html",
    "worker-replace-global-constructor.html",
    "worker-replace-self.html",
    "worker-script-error.html",
    "worker-terminate.html",
    "worker-timeout.html"
  };

  FilePath fast_test_dir;
  fast_test_dir = fast_test_dir.AppendASCII("LayoutTests");
  fast_test_dir = fast_test_dir.AppendASCII("fast");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(fast_test_dir, worker_test_dir, false);

  // Worker tests also rely on common files in js/resources.
  FilePath js_dir = fast_test_dir.AppendASCII("js");
  FilePath resource_dir;
  resource_dir = resource_dir.AppendASCII("resources");
  AddResourceForLayoutTest(js_dir, resource_dir);

  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], false);
}

TEST_F(WorkerTest, WorkerHttpLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    // flakey? BUG 16934 "text-encoding.html",
#if defined(OS_WIN)
    // Fails on the mac (and linux?):
    // http://code.google.com/p/chromium/issues/detail?id=22599
    "worker-importScripts.html",
#endif
    "worker-redirect.html",
  };

  FilePath http_test_dir;
  http_test_dir = http_test_dir.AppendASCII("LayoutTests");
  http_test_dir = http_test_dir.AppendASCII("http");
  http_test_dir = http_test_dir.AppendASCII("tests");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(http_test_dir, worker_test_dir, true);

  StartHttpServer(new_http_root_dir_);
  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], true);
  StopHttpServer();
}

TEST_F(WorkerTest, WorkerXhrHttpLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    "abort-exception-assert.html",
#if defined(OS_WIN)
    // Fails on the mac (and linux?):
    // http://code.google.com/p/chromium/issues/detail?id=22599
    "close.html",
#endif
    //"methods-async.html",
    //"methods.html",
    "xmlhttprequest-file-not-found.html"
  };

  FilePath http_test_dir;
  http_test_dir = http_test_dir.AppendASCII("LayoutTests");
  http_test_dir = http_test_dir.AppendASCII("http");
  http_test_dir = http_test_dir.AppendASCII("tests");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("xmlhttprequest");
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(http_test_dir, worker_test_dir, true);

  StartHttpServer(new_http_root_dir_);
  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], true);
  StopHttpServer();
}

TEST_F(WorkerTest, MessagePorts) {
  static const char* kLayoutTestFiles[] = {
    "message-channel-gc.html",
    "message-channel-gc-2.html",
    "message-channel-gc-3.html",
    "message-channel-gc-4.html",
    "message-port.html",
    "message-port-clone.html",
    // http://code.google.com/p/chromium/issues/detail?id=23709
    // "message-port-constructor-for-deleted-document.html",
    "message-port-deleted-document.html",
    "message-port-deleted-frame.html",
    // http://crbug.com/23597 (caused by http://trac.webkit.org/changeset/48978)
    // "message-port-inactive-document.html",
    "message-port-multi.html",
    "message-port-no-wrapper.html",
    // Only works with run-webkit-tests --leaks.
    //"message-channel-listener-circular-ownership.html",
  };

  FilePath fast_test_dir;
  fast_test_dir = fast_test_dir.AppendASCII("LayoutTests");
  fast_test_dir = fast_test_dir.AppendASCII("fast");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("events");
  InitializeForLayoutTest(fast_test_dir, worker_test_dir, false);

  // MessagePort tests also rely on common files in js/resources.
  FilePath js_dir = fast_test_dir.AppendASCII("js");
  FilePath resource_dir;
  resource_dir = resource_dir.AppendASCII("resources");
  AddResourceForLayoutTest(js_dir, resource_dir);

  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], false);
}

// Disable LimitPerPage on Linux. Seems to work on Mac though:
// http://code.google.com/p/chromium/issues/detail?id=22608
#if !defined(OS_LINUX)
TEST_F(WorkerTest, LimitPerPage) {
  int max_workers_per_tab = WorkerService::kMaxWorkersPerTabWhenSeparate;
  GURL url = GetTestUrl(L"workers", L"many_workers.html");
  url = GURL(url.spec() + StringPrintf("?count=%d", max_workers_per_tab + 1));

  scoped_refptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->NavigateToURL(url));

  EXPECT_EQ(max_workers_per_tab + 1 + (UITest::in_process_renderer() ? 0 : 1),
            UITest::GetBrowserProcessCount());
}
#endif

TEST_F(WorkerTest, LimitTotal) {
  int max_workers_per_tab = WorkerService::kMaxWorkersPerTabWhenSeparate;
  int total_workers = WorkerService::kMaxWorkersWhenSeparate;

  int tab_count = (total_workers / max_workers_per_tab) + 1;
  GURL url = GetTestUrl(L"workers", L"many_workers.html");
  url = GURL(url.spec() + StringPrintf("?count=%d", max_workers_per_tab));

  scoped_refptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->NavigateToURL(url));
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  for (int i = 1; i < tab_count; ++i)
    window->AppendTab(url);

  // The 1 is for the browser process.
  int number_of_processes = 1 +
      (UITest::in_process_renderer() ? 0 : tab_count);
#if defined(OS_LINUX)
  // On Linux, we also have a zygote process and a sandbox host process.
  number_of_processes += 2;
#endif

  // Check that we didn't create more than the max number of workers.
  EXPECT_EQ(total_workers + number_of_processes,
            UITest::GetBrowserProcessCount());

  // Now close a page and check that the queued workers were started.
  tab->NavigateToURL(GetTestUrl(L"google", L"google.html"));

  EXPECT_EQ(total_workers + number_of_processes,
            UITest::GetBrowserProcessCount());
}
<|endoftext|>


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

#include "base/string_util.h"
#include "chrome/browser/worker_host/worker_service.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_layout_test.h"

static const char kTestCompleteCookie[] = "status";
static const char kTestCompleteSuccess[] = "OK";

#if defined(OS_WIN)
#define MAYBE_LimitTotal FLAKY_LimitTotal
#else
#define MAYBE_LimitTotal LimitTotal
#endif

class WorkerTest : public UILayoutTest {
 protected:
  virtual ~WorkerTest() { }

  void RunTest(const std::wstring& test_case) {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    ASSERT_TRUE(tab.get());

    GURL url = GetTestUrl(L"workers", test_case);
    ASSERT_TRUE(tab->NavigateToURL(url));

    std::string value = WaitUntilCookieNonEmpty(tab.get(), url,
        kTestCompleteCookie, kTestIntervalMs, kTestWaitTimeoutMs);
    ASSERT_STREQ(kTestCompleteSuccess, value.c_str());
  }
};

TEST_F(WorkerTest, SingleWorker) {
  RunTest(L"single_worker.html");
}

TEST_F(WorkerTest, MultipleWorkers) {
  RunTest(L"multi_worker.html");
}

// WorkerFastLayoutTests works on the linux try servers.
#if defined(OS_LINUX)
#define WorkerFastLayoutTests DISABLED_WorkerFastLayoutTests
#endif

TEST_F(WorkerTest, WorkerFastLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    "stress-js-execution.html",
#if defined(OS_WIN)
    // Workers don't properly initialize the V8 stack guard.
    // (http://code.google.com/p/chromium/issues/detail?id=21653).
    "use-machine-stack.html",
#endif
    "worker-call.html",
    // Disabled because cloning ports are too slow in Chromium to meet the
    // thresholds in this test.
    // http://code.google.com/p/chromium/issues/detail?id=22780
    // "worker-cloneport.html",

    "worker-close.html",
    "worker-constructor.html",
    "worker-context-gc.html",
    "worker-context-multi-port.html",
    "worker-event-listener.html",
    "worker-gc.html",
    // worker-lifecycle.html relies on layoutTestController.workerThreadCount
    // which is not currently implemented.
    // "worker-lifecycle.html",
    "worker-location.html",
    "worker-messageport.html",
    // Disabled after r27089 (WebKit merge), http://crbug.com/22947
    // "worker-messageport-gc.html",
    "worker-multi-port.html",
    "worker-navigator.html",
    "worker-replace-global-constructor.html",
    "worker-replace-self.html",
    // Disabled afer r27553 (WebKit merge), http://crbug.com/23391
    // "worker-script-error.html",
    "worker-terminate.html",
    "worker-timeout.html"
  };

  FilePath fast_test_dir;
  fast_test_dir = fast_test_dir.AppendASCII("LayoutTests");
  fast_test_dir = fast_test_dir.AppendASCII("fast");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(fast_test_dir, worker_test_dir, false);

  // Worker tests also rely on common files in js/resources.
  FilePath js_dir = fast_test_dir.AppendASCII("js");
  FilePath resource_dir;
  resource_dir = resource_dir.AppendASCII("resources");
  AddResourceForLayoutTest(js_dir, resource_dir);

  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], false);
}

TEST_F(WorkerTest, WorkerHttpLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    // flakey? BUG 16934 "text-encoding.html",
#if defined(OS_WIN)
    // Fails on the mac (and linux?):
    // http://code.google.com/p/chromium/issues/detail?id=22599
    "worker-importScripts.html",
#endif
    "worker-redirect.html",
  };

  FilePath http_test_dir;
  http_test_dir = http_test_dir.AppendASCII("LayoutTests");
  http_test_dir = http_test_dir.AppendASCII("http");
  http_test_dir = http_test_dir.AppendASCII("tests");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(http_test_dir, worker_test_dir, true);

  StartHttpServer(new_http_root_dir_);
  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], true);
  StopHttpServer();
}

TEST_F(WorkerTest, WorkerXhrHttpLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    "abort-exception-assert.html",
#if defined(OS_WIN)
    // Fails on the mac (and linux?):
    // http://code.google.com/p/chromium/issues/detail?id=22599
    "close.html",
#endif
    //"methods-async.html",
    //"methods.html",
    "xmlhttprequest-file-not-found.html"
  };

  FilePath http_test_dir;
  http_test_dir = http_test_dir.AppendASCII("LayoutTests");
  http_test_dir = http_test_dir.AppendASCII("http");
  http_test_dir = http_test_dir.AppendASCII("tests");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("xmlhttprequest");
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(http_test_dir, worker_test_dir, true);

  StartHttpServer(new_http_root_dir_);
  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], true);
  StopHttpServer();
}

TEST_F(WorkerTest, MessagePorts) {
  static const char* kLayoutTestFiles[] = {
    "message-channel-gc.html",
    "message-channel-gc-2.html",
    "message-channel-gc-3.html",
    "message-channel-gc-4.html",
    "message-port.html",
    "message-port-clone.html",
    // http://code.google.com/p/chromium/issues/detail?id=23709
    // "message-port-constructor-for-deleted-document.html",
    "message-port-deleted-document.html",
    "message-port-deleted-frame.html",
    // http://crbug.com/23597 (caused by http://trac.webkit.org/changeset/48978)
    // "message-port-inactive-document.html",
    "message-port-multi.html",
    "message-port-no-wrapper.html",
    // Only works with run-webkit-tests --leaks.
    //"message-channel-listener-circular-ownership.html",
  };

  FilePath fast_test_dir;
  fast_test_dir = fast_test_dir.AppendASCII("LayoutTests");
  fast_test_dir = fast_test_dir.AppendASCII("fast");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("events");
  InitializeForLayoutTest(fast_test_dir, worker_test_dir, false);

  // MessagePort tests also rely on common files in js/resources.
  FilePath js_dir = fast_test_dir.AppendASCII("js");
  FilePath resource_dir;
  resource_dir = resource_dir.AppendASCII("resources");
  AddResourceForLayoutTest(js_dir, resource_dir);

  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], false);
}

// Disable LimitPerPage on Linux. Seems to work on Mac though:
// http://code.google.com/p/chromium/issues/detail?id=22608
#if !defined(OS_LINUX)
// This test fails after WebKit merge 49414:49432. (BUG=24652)
TEST_F(WorkerTest, LimitPerPage) {
  int max_workers_per_tab = WorkerService::kMaxWorkersPerTabWhenSeparate;
  GURL url = GetTestUrl(L"workers", L"many_workers.html");
  url = GURL(url.spec() + StringPrintf("?count=%d", max_workers_per_tab + 1));

  scoped_refptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->NavigateToURL(url));

  EXPECT_EQ(max_workers_per_tab + 1 + (UITest::in_process_renderer() ? 0 : 1),
            UITest::GetBrowserProcessCount());
}
#endif

// This test fails after WebKit merge 49414:49432. (BUG=24652)
TEST_F(WorkerTest, LimitTotal) {
  int max_workers_per_tab = WorkerService::kMaxWorkersPerTabWhenSeparate;
  int total_workers = WorkerService::kMaxWorkersWhenSeparate;

  int tab_count = (total_workers / max_workers_per_tab) + 1;
  GURL url = GetTestUrl(L"workers", L"many_workers.html");
  url = GURL(url.spec() + StringPrintf("?count=%d", max_workers_per_tab));

  scoped_refptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->NavigateToURL(url));
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  for (int i = 1; i < tab_count; ++i)
    window->AppendTab(url);

  // The 1 is for the browser process.
  int number_of_processes = 1 +
      (UITest::in_process_renderer() ? 0 : tab_count);
#if defined(OS_LINUX)
  // On Linux, we also have a zygote process and a sandbox host process.
  number_of_processes += 2;
#endif

  // Check that we didn't create more than the max number of workers.
  EXPECT_EQ(total_workers + number_of_processes,
            UITest::GetBrowserProcessCount());

  // Now close a page and check that the queued workers were started.
  tab->NavigateToURL(GetTestUrl(L"google", L"google.html"));

  EXPECT_EQ(total_workers + number_of_processes,
            UITest::GetBrowserProcessCount());
}
<commit_msg>Try to remove the flakiness from the WorkerLimit ui test by polling for process count change.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/string_util.h"
#include "chrome/browser/worker_host/worker_service.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_layout_test.h"

static const char kTestCompleteCookie[] = "status";
static const char kTestCompleteSuccess[] = "OK";

class WorkerTest : public UILayoutTest {
 protected:
  virtual ~WorkerTest() { }

  void RunTest(const std::wstring& test_case) {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    ASSERT_TRUE(tab.get());

    GURL url = GetTestUrl(L"workers", test_case);
    ASSERT_TRUE(tab->NavigateToURL(url));

    std::string value = WaitUntilCookieNonEmpty(tab.get(), url,
        kTestCompleteCookie, kTestIntervalMs, kTestWaitTimeoutMs);
    ASSERT_STREQ(kTestCompleteSuccess, value.c_str());
  }

  bool WaitForProcessCountToBe(int tabs, int workers) {
    // The 1 is for the browser process.
    int number_of_processes = 1 + workers +
        (UITest::in_process_renderer() ? 0 : tabs);
#if defined(OS_LINUX)
    // On Linux, we also have a zygote process and a sandbox host process.
    number_of_processes += 2;
#endif

    int cur_process_count;
    for (int i = 0; i < 10; ++i) {
      cur_process_count = GetBrowserProcessCount();
      if (cur_process_count == number_of_processes)
        return true;

      PlatformThread::Sleep(sleep_timeout_ms() / 10);
    }

    EXPECT_EQ(number_of_processes, cur_process_count);
    return false;
  }
};

TEST_F(WorkerTest, SingleWorker) {
  RunTest(L"single_worker.html");
}

TEST_F(WorkerTest, MultipleWorkers) {
  RunTest(L"multi_worker.html");
}

// WorkerFastLayoutTests works on the linux try servers.
#if defined(OS_LINUX)
#define WorkerFastLayoutTests DISABLED_WorkerFastLayoutTests
#endif

TEST_F(WorkerTest, WorkerFastLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    "stress-js-execution.html",
#if defined(OS_WIN)
    // Workers don't properly initialize the V8 stack guard.
    // (http://code.google.com/p/chromium/issues/detail?id=21653).
    "use-machine-stack.html",
#endif
    "worker-call.html",
    // Disabled because cloning ports are too slow in Chromium to meet the
    // thresholds in this test.
    // http://code.google.com/p/chromium/issues/detail?id=22780
    // "worker-cloneport.html",

    "worker-close.html",
    "worker-constructor.html",
    "worker-context-gc.html",
    "worker-context-multi-port.html",
    "worker-event-listener.html",
    "worker-gc.html",
    // worker-lifecycle.html relies on layoutTestController.workerThreadCount
    // which is not currently implemented.
    // "worker-lifecycle.html",
    "worker-location.html",
    "worker-messageport.html",
    // Disabled after r27089 (WebKit merge), http://crbug.com/22947
    // "worker-messageport-gc.html",
    "worker-multi-port.html",
    "worker-navigator.html",
    "worker-replace-global-constructor.html",
    "worker-replace-self.html",
    // Disabled afer r27553 (WebKit merge), http://crbug.com/23391
    // "worker-script-error.html",
    "worker-terminate.html",
    "worker-timeout.html"
  };

  FilePath fast_test_dir;
  fast_test_dir = fast_test_dir.AppendASCII("LayoutTests");
  fast_test_dir = fast_test_dir.AppendASCII("fast");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(fast_test_dir, worker_test_dir, false);

  // Worker tests also rely on common files in js/resources.
  FilePath js_dir = fast_test_dir.AppendASCII("js");
  FilePath resource_dir;
  resource_dir = resource_dir.AppendASCII("resources");
  AddResourceForLayoutTest(js_dir, resource_dir);

  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], false);
}

TEST_F(WorkerTest, WorkerHttpLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    // flakey? BUG 16934 "text-encoding.html",
#if defined(OS_WIN)
    // Fails on the mac (and linux?):
    // http://code.google.com/p/chromium/issues/detail?id=22599
    "worker-importScripts.html",
#endif
    "worker-redirect.html",
  };

  FilePath http_test_dir;
  http_test_dir = http_test_dir.AppendASCII("LayoutTests");
  http_test_dir = http_test_dir.AppendASCII("http");
  http_test_dir = http_test_dir.AppendASCII("tests");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(http_test_dir, worker_test_dir, true);

  StartHttpServer(new_http_root_dir_);
  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], true);
  StopHttpServer();
}

TEST_F(WorkerTest, WorkerXhrHttpLayoutTests) {
  static const char* kLayoutTestFiles[] = {
    "abort-exception-assert.html",
#if defined(OS_WIN)
    // Fails on the mac (and linux?):
    // http://code.google.com/p/chromium/issues/detail?id=22599
    "close.html",
#endif
    //"methods-async.html",
    //"methods.html",
    "xmlhttprequest-file-not-found.html"
  };

  FilePath http_test_dir;
  http_test_dir = http_test_dir.AppendASCII("LayoutTests");
  http_test_dir = http_test_dir.AppendASCII("http");
  http_test_dir = http_test_dir.AppendASCII("tests");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("xmlhttprequest");
  worker_test_dir = worker_test_dir.AppendASCII("workers");
  InitializeForLayoutTest(http_test_dir, worker_test_dir, true);

  StartHttpServer(new_http_root_dir_);
  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], true);
  StopHttpServer();
}

TEST_F(WorkerTest, MessagePorts) {
  static const char* kLayoutTestFiles[] = {
    "message-channel-gc.html",
    "message-channel-gc-2.html",
    "message-channel-gc-3.html",
    "message-channel-gc-4.html",
    "message-port.html",
    "message-port-clone.html",
    // http://code.google.com/p/chromium/issues/detail?id=23709
    // "message-port-constructor-for-deleted-document.html",
    "message-port-deleted-document.html",
    "message-port-deleted-frame.html",
    // http://crbug.com/23597 (caused by http://trac.webkit.org/changeset/48978)
    // "message-port-inactive-document.html",
    "message-port-multi.html",
    "message-port-no-wrapper.html",
    // Only works with run-webkit-tests --leaks.
    //"message-channel-listener-circular-ownership.html",
  };

  FilePath fast_test_dir;
  fast_test_dir = fast_test_dir.AppendASCII("LayoutTests");
  fast_test_dir = fast_test_dir.AppendASCII("fast");

  FilePath worker_test_dir;
  worker_test_dir = worker_test_dir.AppendASCII("events");
  InitializeForLayoutTest(fast_test_dir, worker_test_dir, false);

  // MessagePort tests also rely on common files in js/resources.
  FilePath js_dir = fast_test_dir.AppendASCII("js");
  FilePath resource_dir;
  resource_dir = resource_dir.AppendASCII("resources");
  AddResourceForLayoutTest(js_dir, resource_dir);

  for (size_t i = 0; i < arraysize(kLayoutTestFiles); ++i)
    RunLayoutTest(kLayoutTestFiles[i], false);
}

// Disable LimitPerPage on Linux. Seems to work on Mac though:
// http://code.google.com/p/chromium/issues/detail?id=22608
#if !defined(OS_LINUX)
// This test fails after WebKit merge 49414:49432. (BUG=24652)
TEST_F(WorkerTest, LimitPerPage) {
  int max_workers_per_tab = WorkerService::kMaxWorkersPerTabWhenSeparate;
  GURL url = GetTestUrl(L"workers", L"many_workers.html");
  url = GURL(url.spec() + StringPrintf("?count=%d", max_workers_per_tab + 1));

  scoped_refptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->NavigateToURL(url));

  EXPECT_EQ(max_workers_per_tab + 1 + (UITest::in_process_renderer() ? 0 : 1),
            UITest::GetBrowserProcessCount());
}
#endif

TEST_F(WorkerTest, LimitTotal) {
  int max_workers_per_tab = WorkerService::kMaxWorkersPerTabWhenSeparate;
  int total_workers = WorkerService::kMaxWorkersWhenSeparate;

  int tab_count = (total_workers / max_workers_per_tab) + 1;
  GURL url = GetTestUrl(L"workers", L"many_workers.html");
  url = GURL(url.spec() + StringPrintf("?count=%d", max_workers_per_tab));

  scoped_refptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab.get());
  ASSERT_TRUE(tab->NavigateToURL(url));
  scoped_refptr<BrowserProxy> window(automation()->GetBrowserWindow(0));
  for (int i = 1; i < tab_count; ++i)
    window->AppendTab(url);

  // Check that we didn't create more than the max number of workers.
  ASSERT_TRUE(WaitForProcessCountToBe(tab_count, total_workers));

  // Now close a page and check that the queued workers were started.
  tab->NavigateToURL(GetTestUrl(L"google", L"google.html"));

  ASSERT_TRUE(WaitForProcessCountToBe(tab_count, total_workers));
}
<|endoftext|>