<commit_before>/*

  Safety Framework for Component-based Robotics

  Created on: August 13, 2012

  Copyright (C) 2012-2013 Min Yang Jung, Peter Kazanzides

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE_1_0.txt or copy at
  http://www.boost.org/LICENSE_1_0.txt)

*/

#include "config.h"
#include "common.h"
#include "json.h"
#include "monitor.h"
#include "cisstMonitor.h"
#include "cisstEventLocation.h"

#include <cisstCommon/cmnGetChar.h>
#include <cisstOSAbstraction/osaSleep.h>
#include <cisstMultiTask/mtsCollectorState.h>
#include <cisstMultiTask/mtsTaskPeriodic.h>
#include <cisstMultiTask/mtsTaskManager.h>
#include <cisstMultiTask/mtsInterfaceProvided.h>
#if (CISST_OS == CISST_LINUX_XENOMAI)
#include <sys/mman.h>
#endif

using namespace SF;

class PeriodicTask: public mtsTaskPeriodic {
protected:
    double SleepDuration;

public:
    PeriodicTask(const std::string & name, double period) :
        mtsTaskPeriodic(name, period, false, 5000)
    {
        SleepDuration = 0.0;

        StateTable.AddData(SleepDuration, "SleepDuration");

        mtsInterfaceProvided * provided = AddInterfaceProvided("CustomInterface");
        if (provided) {
            provided->AddCommandReadState(StateTable, SleepDuration, "SleepDuration");
        }
    }

    ~PeriodicTask() {}

    void Configure(const std::string & CMN_UNUSED(filename) = "") {}
    void Startup(void) {}
    void Run(void) {
        ProcessQueuedCommands();
        ProcessQueuedEvents();
        //std::cout << "." << std::flush;
        /*
        static int count = 0;
        if (count++ % 10 == 0) {
            this->GenerateFaultEvent(std::string("MY FAULT EVENT"));
        }
        */
        static double T = (1.0 / this->Period) * 60.0 * 60.0;
        static int i = 0;

        SleepDuration = this->Period * (0.8 * sin(2 * cmnPI * ((double) ++i / T)));
        osaSleep(SleepDuration);
    }
    void Cleanup(void) {}
};

// Create periodic task
bool CreatePeriodicThread(const std::string & componentName, double period);
// to monitor values in real-time
bool InstallMonitor(const std::string & targetComponentName, unsigned int frequency);

// Local component manager
mtsManagerLocal * ComponentManager = 0;
// Test periodic task
PeriodicTask * task = 0;

int main(int argc, char *argv[])
{
#if (CISST_OS == CISST_LINUX_XENOMAI)
    mlockall(MCL_CURRENT|MCL_FUTURE);
#endif

#if SF_USE_G2LOG
    // Logger setup
    g2LogWorker logger(argv[0], "./");
    g2::initializeLogging(&logger);
    std::cout << "Log file: \"" << logger.logFileName() << "\"\n" << std::endl;
#endif

    cmnLogger::SetMask(CMN_LOG_ALLOW_ALL);
    cmnLogger::SetMaskFunction(CMN_LOG_ALLOW_ALL);
    cmnLogger::SetMaskDefaultLog(CMN_LOG_ALLOW_ALL);
    //cmnLogger::AddChannel(std::cout, CMN_LOG_ALLOW_ERRORS_AND_WARNINGS);
    cmnLogger::AddChannel(std::cout, CMN_LOG_ALLOW_ALL);
    cmnLogger::SetMaskClassMatching("mtsSafetyCoordinator", CMN_LOG_ALLOW_ALL);
    cmnLogger::SetMaskClassMatching("mtsMonitorComponent", CMN_LOG_ALLOW_ALL);

    // Get instance of the cisst Component Manager
    mtsComponentManager::InstallSafetyCoordinator();
    try {
        ComponentManager = mtsComponentManager::GetInstance();
    } catch (...) {
        SFLOG_ERROR << "Failed to initialize local component manager" << std::endl;
        return 1;
    }

    // Print information about middleware(s) available
    StrVecType info;
    GetMiddlewareInfo(info);
    std::cout << "Middleware(s) detected: ";
    if (info.size() == 0) {
        std::cout << "none" << std::endl;
    } else {
        std::cout << std::endl;
        for (size_t i = 0; i < info.size(); ++i) {
            std::cout << "[" << (i+1) << "] " << info[i] << std::endl;
        }
    }
    std::cout << std::endl;

    // Create five test components with different T
    std::vector<unsigned int> f; // Hz
#if 0
    f.push_back(1);
    f.push_back(2);
    f.push_back(5);
    f.push_back(10);
    f.push_back(20);
    f.push_back(50);
    f.push_back(100);
    f.push_back(200);
    f.push_back(500);
#endif
    f.push_back(1);

    std::string componentName;
    std::stringstream ss;
    for (size_t i = 0; i < f.size(); ++i) {
        ss.str("");
        ss << "Component" << f[i];
        componentName = ss.str();

        // Create periodic task
        if (!CreatePeriodicThread(componentName, 1.0 / (double)f[i])) {
            SFLOG_ERROR << "Failed to add periodic component \"" << componentName << "\"" << std::endl;
            return 1;
        }
        // Install monitor
        if (!InstallMonitor(componentName, f[i])) {
            SFLOG_ERROR << "Failed to install monitor for periodic component \"" << componentName << "\"" << std::endl;
            return 1;
        }
    }

    if (ComponentManager->GetCoordinator()) {
        if (!ComponentManager->GetCoordinator()->DeployMonitorsAndFDDs()) {
            SFLOG_ERROR << "Failed to deploy monitors and FDDs" << std::endl;
            return 1;
        }
    } else {
        SFLOG_ERROR  << "Failed to get coordinator in this process";
        return 1;
    }

    // Create and run all components
    ComponentManager->CreateAll();
    ComponentManager->WaitForStateAll(mtsComponentState::READY);

    ComponentManager->StartAll();
    ComponentManager->WaitForStateAll(mtsComponentState::ACTIVE);

    std::cout << "Press 'q' to quit or click CLOSE button of the visualizer." << std::endl;
    std::cout << "Running periodic tasks ";

    // loop until 'q' is pressed
    int key = ' ';
    while (key != 'q') {
        key = cmnGetChar();
        osaSleep(100 * cmn_ms);
    }
    std::cout << std::endl;

    // Clean up resources
    SFLOG_INFO << "Cleaning up..." << std::endl;

#if (CISST_OS != CISST_LINUX_XENOMAI)
    ComponentManager->KillAll();
    ComponentManager->WaitForStateAll(mtsComponentState::FINISHED, 2.0 * cmn_s);
#endif

    ComponentManager->Cleanup();

    return 0;
}

// Create periodic thread
bool CreatePeriodicThread(const std::string & componentName, double period)
{
    // Create periodic thread
    task = new PeriodicTask(componentName, period);
    if (!ComponentManager->AddComponent(task)) {
        SFLOG_ERROR << "Failed to add component \"" << componentName << "\"" << std::endl;
        return false;
    }

    return true;
}

bool InstallMonitor(const std::string & targetComponentName, unsigned int frequency)
{
    if (!ComponentManager->GetCoordinator()) {
        SFLOG_ERROR  << "Failed to get coordinator in this process";
        return false;
    }

    // Define target
#if 0
    cisstEventLocation * locationID = new cisstEventLocation;
    locationID->SetProcessName(ComponentManager->GetProcessName());
    locationID->SetComponentName(targetComponentName);

    cisstMonitor * monitor;

    // Install monitor for timing fault - period
    monitor = new cisstMonitor(Monitor::TARGET_THREAD_PERIOD,
                               locationID,
                               Monitor::STATE_ON,
                               Monitor::OUTPUT_STREAM,
                               frequency);
    // MJ TODO: Run system for a few minutes, collect experimental data,
    // and determine variance of period with upper/lower limits and thresholds.
    if (!ComponentManager->GetCoordinator()->AddMonitorTarget(monitor)) {
        SFLOG_ERROR << "Failed to add new monitor target for component \"" << targetComponentName << "\"" << std::endl;
        SFLOG_ERROR << "JSON: " << monitor->GetMonitorJSON() << std::endl;
        return false;
    }
    SFLOG_INFO << "Successfully added monitor target: " << *monitor << std::endl;

    // Install monitor for execution time (user)
    monitor = new cisstMonitor(Monitor::TARGET_THREAD_DUTYCYCLE_USER,
                               locationID,
                               Monitor::STATE_ON,
                               Monitor::OUTPUT_STREAM,
                               frequency);

    if (!ComponentManager->GetCoordinator()->AddMonitorTarget(monitor)) {
        SFLOG_ERROR << "Failed to add new monitor target for component \"" << targetComponentName << "\"" << std::endl;
        SFLOG_ERROR << "JSON: " << monitor->GetMonitorJSON() << std::endl;
        return false;
    }
    SFLOG_INFO << "Successfully added monitor target: " << *monitor << std::endl;

    // Install monitor for execution time (total)
    monitor = new cisstMonitor(Monitor::TARGET_THREAD_DUTYCYCLE_TOTAL,
                               locationID,
                               Monitor::STATE_ON,
                               Monitor::OUTPUT_STREAM,
                               frequency);

    if (!ComponentManager->GetCoordinator()->AddMonitorTarget(monitor)) {
        SFLOG_ERROR << "Failed to add new monitor target for component \"" << targetComponentName << "\"" << std::endl;
        SFLOG_ERROR << "JSON: " << monitor->GetMonitorJSON() << std::endl;
        return false;
    }
    SFLOG_INFO << "Successfully added monitor target: " << *monitor << std::endl;
#else
    const std::string jsonFileName(SF_SOURCE_ROOT_DIR"/examples/monitor/monitor.json");
    if (!ComponentManager->GetCoordinator()->AddMonitorTarget(jsonFileName)) {
        SFLOG_ERROR << "Failed to load monitoring target file: \"" << jsonFileName << "\"" << std::endl;
        return false;
    }
#endif

    return true;
}
<commit_msg>examples/monitor: Added tests for various cisst payload types, minor code cleanup<commit_after>/*

  Safety Framework for Component-based Robotics

  Created on: August 13, 2012

  Copyright (C) 2012-2013 Min Yang Jung, Peter Kazanzides

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE_1_0.txt or copy at
  http://www.boost.org/LICENSE_1_0.txt)

*/

#include "config.h"
#include "common.h"
#include "json.h"
#include "monitor.h"
#include "cisstMonitor.h"
#include "cisstEventLocation.h"

#include <cisstCommon/cmnGetChar.h>
#include <cisstOSAbstraction/osaSleep.h>
#include <cisstMultiTask/mtsCollectorState.h>
#include <cisstMultiTask/mtsTaskPeriodic.h>
#include <cisstMultiTask/mtsTaskManager.h>
#include <cisstMultiTask/mtsInterfaceProvided.h>
#include <cisstMultiTask/mtsFixedSizeVectorTypes.h>
#if (CISST_OS == CISST_LINUX_XENOMAI)
#include <sys/mman.h>
#endif

using namespace SF;

class PeriodicTask: public mtsTaskPeriodic {
protected:
    //double SleepDuration;
    //mtsDouble SleepDuration;
    //mtsDouble2 SleepDuration;
    vctDoubleVec SleepDuration;

    mtsDouble       d0;
    mtsDouble1      d1;
    mtsDouble2      d2;
    mtsInt          i;
    mtsInt1         i1;
    mtsInt2         i2;
    mtsVctDoubleVec vd;
    mtsVctIntVec    vi;
    mtsVct3         v3;
    mtsVct3         v5;

public:
    PeriodicTask(const std::string & name, double period) :
        mtsTaskPeriodic(name, period, false, 5000)
    {
        //SleepDuration = 10.0;
        //SleepDuration(0) = 100.0;
        //SleepDuration(1) = 1000.0;
        SleepDuration.SetSize(5);

        StateTable.AddData(SleepDuration, "SleepDuration");
#if 0
        StateTable.AddData(d0, "SleepDuration");
        StateTable.AddData(d1, "SleepDuration");
        StateTable.AddData(d2, "SleepDuration");
        StateTable.AddData(i,  "SleepDuration");
        StateTable.AddData(i1, "SleepDuration");
        StateTable.AddData(i2, "SleepDuration");
        StateTable.AddData(vd, "SleepDuration");
        StateTable.AddData(vi, "SleepDuration");
        StateTable.AddData(v3, "SleepDuration");
        StateTable.AddData(v5, "SleepDuration");
#endif

        mtsInterfaceProvided * provided = AddInterfaceProvided("CustomInterface");
        if (provided) {
            provided->AddCommandReadState(StateTable, SleepDuration, "SleepDuration");
        }
    }

    ~PeriodicTask() {}

    void Configure(const std::string & CMN_UNUSED(filename) = "") {}
    void Startup(void) {}
    void Run(void) {
        ProcessQueuedCommands();
        ProcessQueuedEvents();
        //std::cout << "." << std::flush;
        /*
        static int count = 0;
        if (count++ % 10 == 0) {
            this->GenerateFaultEvent(std::string("MY FAULT EVENT"));
        }
        */
        static double T = (1.0 / this->Period) * 60.0 * 60.0;
        static int i = 0;

        //SleepDuration = this->Period * (0.8 * sin(2 * cmnPI * ((double) ++i / T)));
        //osaSleep(SleepDuration);

        //SleepDuration(0) += 0.1;
        //SleepDuration(1) += 0.1;
        //SleepDuration += 0.2;
        SleepDuration(0) += 0.1;
        SleepDuration(1) += 0.2;
        SleepDuration(2) += 0.3;
        SleepDuration(3) += 0.4;
        SleepDuration(4) += 0.5;
    }
    void Cleanup(void) {}
};

// Create periodic task
bool CreatePeriodicThread(const std::string & componentName, double period);
// to monitor values in real-time
bool InstallMonitor(const std::string & targetComponentName, unsigned int frequency);

// Local component manager
mtsManagerLocal * ComponentManager = 0;
// Test periodic task
PeriodicTask * task = 0;

int main(int argc, char *argv[])
{
#if SF_USE_G2LOG
    // Logger setup
    g2LogWorker logger(argv[0], "./");
    g2::initializeLogging(&logger);
    std::cout << "Log file: \"" << logger.logFileName() << "\"\n" << std::endl;
#endif

    cmnLogger::SetMask(CMN_LOG_ALLOW_ALL);
    cmnLogger::SetMaskFunction(CMN_LOG_ALLOW_ALL);
    cmnLogger::SetMaskDefaultLog(CMN_LOG_ALLOW_ALL);
    //cmnLogger::AddChannel(std::cout, CMN_LOG_ALLOW_ERRORS_AND_WARNINGS);
    cmnLogger::AddChannel(std::cout, CMN_LOG_ALLOW_ALL);
    cmnLogger::SetMaskClassMatching("mtsSafetyCoordinator", CMN_LOG_ALLOW_ALL);
    cmnLogger::SetMaskClassMatching("mtsMonitorComponent", CMN_LOG_ALLOW_ALL);

    // Get instance of the cisst Component Manager
    mtsComponentManager::InstallSafetyCoordinator();
    try {
        ComponentManager = mtsComponentManager::GetInstance();
    } catch (...) {
        SFLOG_ERROR << "Failed to initialize local component manager" << std::endl;
        return 1;
    }

    // Print information about middleware(s) available
    StrVecType info;
    GetMiddlewareInfo(info);
    std::cout << "Middleware(s) detected: ";
    if (info.size() == 0) {
        std::cout << "none" << std::endl;
    } else {
        std::cout << std::endl;
        for (size_t i = 0; i < info.size(); ++i) {
            std::cout << "[" << (i+1) << "] " << info[i] << std::endl;
        }
    }
    std::cout << std::endl;

    // Create five test components with different T
    std::vector<unsigned int> f; // Hz
#if 0
    f.push_back(1);
    f.push_back(2);
    f.push_back(5);
    f.push_back(10);
    f.push_back(20);
    f.push_back(50);
    f.push_back(100);
    f.push_back(200);
    f.push_back(500);
#endif
    f.push_back(1);

    std::string componentName;
    std::stringstream ss;
    for (size_t i = 0; i < f.size(); ++i) {
        ss.str("");
        ss << "Component" << f[i];
        componentName = ss.str();

        // Create periodic task
        if (!CreatePeriodicThread(componentName, 1.0 / (double)f[i])) {
            SFLOG_ERROR << "Failed to add periodic component \"" << componentName << "\"" << std::endl;
            return 1;
        }
        // Install monitor
        if (!InstallMonitor(componentName, f[i])) {
            SFLOG_ERROR << "Failed to install monitor for periodic component \"" << componentName << "\"" << std::endl;
            return 1;
        }
    }

    if (ComponentManager->GetCoordinator()) {
        if (!ComponentManager->GetCoordinator()->DeployMonitorsAndFDDs()) {
            SFLOG_ERROR << "Failed to deploy monitors and FDDs" << std::endl;
            return 1;
        }
    } else {
        SFLOG_ERROR  << "Failed to get coordinator in this process";
        return 1;
    }

    // Create and run all components
    ComponentManager->CreateAll();
    ComponentManager->WaitForStateAll(mtsComponentState::READY);

    ComponentManager->StartAll();
    ComponentManager->WaitForStateAll(mtsComponentState::ACTIVE);

    std::cout << "Press 'q' to quit or click CLOSE button of the visualizer." << std::endl;
    std::cout << "Running periodic tasks ";

    // loop until 'q' is pressed
    int key = ' ';
    while (key != 'q') {
        key = cmnGetChar();
        osaSleep(100 * cmn_ms);
    }
    std::cout << std::endl;

    // Clean up resources
    SFLOG_INFO << "Cleaning up..." << std::endl;

#if (CISST_OS != CISST_LINUX_XENOMAI)
    ComponentManager->KillAll();
    ComponentManager->WaitForStateAll(mtsComponentState::FINISHED, 2.0 * cmn_s);
#endif

    ComponentManager->Cleanup();

    return 0;
}

// Create periodic thread
bool CreatePeriodicThread(const std::string & componentName, double period)
{
    // Create periodic thread
    task = new PeriodicTask(componentName, period);
    if (!ComponentManager->AddComponent(task)) {
        SFLOG_ERROR << "Failed to add component \"" << componentName << "\"" << std::endl;
        return false;
    }

    return true;
}

bool InstallMonitor(const std::string & targetComponentName, unsigned int frequency)
{
    mtsSafetyCoordinator * coordinator = ComponentManager->GetCoordinator();
    if (!coordinator) {
        SFLOG_ERROR  << "Failed to get coordinator in this process";
        return false;
    }

    // Define target
#if 0
    cisstEventLocation * locationID = new cisstEventLocation;
    locationID->SetProcessName(ComponentManager->GetProcessName());
    locationID->SetComponentName(targetComponentName);

    cisstMonitor * monitor;

    // Install monitor for timing fault - period
    monitor = new cisstMonitor(Monitor::TARGET_THREAD_PERIOD,
                               locationID,
                               Monitor::STATE_ON,
                               Monitor::OUTPUT_STREAM,
                               frequency);
    // MJ TODO: Run system for a few minutes, collect experimental data,
    // and determine variance of period with upper/lower limits and thresholds.
    if (!coordinator->AddMonitorTarget(monitor)) {
        SFLOG_ERROR << "Failed to add new monitor target for component \"" << targetComponentName << "\"" << std::endl;
        SFLOG_ERROR << "JSON: " << monitor->GetMonitorJSON() << std::endl;
        return false;
    }
    SFLOG_INFO << "Successfully added monitor target: " << *monitor << std::endl;

    // Install monitor for execution time (user)
    monitor = new cisstMonitor(Monitor::TARGET_THREAD_DUTYCYCLE_USER,
                               locationID,
                               Monitor::STATE_ON,
                               Monitor::OUTPUT_STREAM,
                               frequency);

    if (!coordinator->AddMonitorTarget(monitor)) {
        SFLOG_ERROR << "Failed to add new monitor target for component \"" << targetComponentName << "\"" << std::endl;
        SFLOG_ERROR << "JSON: " << monitor->GetMonitorJSON() << std::endl;
        return false;
    }
    SFLOG_INFO << "Successfully added monitor target: " << *monitor << std::endl;

    // Install monitor for execution time (total)
    monitor = new cisstMonitor(Monitor::TARGET_THREAD_DUTYCYCLE_TOTAL,
                               locationID,
                               Monitor::STATE_ON,
                               Monitor::OUTPUT_STREAM,
                               frequency);

    if (!coordinator->AddMonitorTarget(monitor)) {
        SFLOG_ERROR << "Failed to add new monitor target for component \"" << targetComponentName << "\"" << std::endl;
        SFLOG_ERROR << "JSON: " << monitor->GetMonitorJSON() << std::endl;
        return false;
    }
    SFLOG_INFO << "Successfully added monitor target: " << *monitor << std::endl;
#else
    const std::string jsonFileName(SF_SOURCE_ROOT_DIR"/examples/monitor/monitor.json");
    if (!coordinator->AddMonitorTargetFromJSONFile(jsonFileName)) {
        SFLOG_ERROR << "Failed to load monitoring target file: \"" << jsonFileName << "\"" << std::endl;
        return false;
    }
#endif

    return true;
}
<|endoftext|>


<commit_before>// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "serialize-async.h"
#include "serialize.h"
#include <kj/debug.h>
#include <kj/thread.h>
#include <kj/async-unix.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "test-util.h"
#include <gtest/gtest.h>

namespace capnp {
namespace _ {  // private
namespace {

class FragmentingOutputStream: public kj::OutputStream {
public:
  FragmentingOutputStream(kj::OutputStream& inner): inner(inner) {}

  void write(const void* buffer, size_t size) override {
    while (size > 0) {
      size_t n = rand() % size + 1;
      inner.write(buffer, n);
      usleep(5);
      buffer = reinterpret_cast<const byte*>(buffer) + n;
      size -= n;
    }
  }

private:
  kj::OutputStream& inner;
};

class TestMessageBuilder: public MallocMessageBuilder {
  // A MessageBuilder that tries to allocate an exact number of total segments, by allocating
  // minimum-size segments until it reaches the number, then allocating one large segment to
  // finish.

public:
  explicit TestMessageBuilder(uint desiredSegmentCount)
      : MallocMessageBuilder(0, AllocationStrategy::FIXED_SIZE),
        desiredSegmentCount(desiredSegmentCount) {}
  ~TestMessageBuilder() {
    EXPECT_EQ(0u, desiredSegmentCount);
  }

  kj::ArrayPtr<word> allocateSegment(uint minimumSize) override {
    if (desiredSegmentCount <= 1) {
      if (desiredSegmentCount < 1) {
        ADD_FAILURE() << "Allocated more segments than desired.";
      } else {
        --desiredSegmentCount;
      }
      return MallocMessageBuilder::allocateSegment(8192);
    } else {
      --desiredSegmentCount;
      return MallocMessageBuilder::allocateSegment(minimumSize);
    }
  }

private:
  uint desiredSegmentCount;
};

class SerializeAsyncTest: public testing::Test {
protected:
  int fds[2];

  SerializeAsyncTest() {
    // Use a socketpair rather than a pipe so that we can set the buffer size extremely small.
    KJ_SYSCALL(socketpair(AF_UNIX, SOCK_STREAM, 0, fds));

    KJ_SYSCALL(shutdown(fds[0], SHUT_WR));
    KJ_SYSCALL(shutdown(fds[1], SHUT_RD));

    // Request that the buffer size be as small as possible, to force the event loop to kick in.
    uint zero = 0;
    KJ_SYSCALL(setsockopt(fds[0], SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero)));
    KJ_SYSCALL(setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, &zero, sizeof(zero)));
  }
  ~SerializeAsyncTest() {
    close(fds[0]);
    close(fds[1]);
  }
};

TEST_F(SerializeAsyncTest, ParseAsync) {
  kj::UnixEventLoop loop;

  auto input = kj::AsyncInputStream::wrapFd(fds[0]);
  kj::FdOutputStream rawOutput(fds[1]);
  FragmentingOutputStream output(rawOutput);

  TestMessageBuilder message(1);
  initTestMessage(message.getRoot<TestAllTypes>());

  auto promise = loop.evalLater([&]() {
    return readMessage(*input);
  });

  kj::Thread thread([&]() {
    writeMessage(output, message);
  });

  auto received = loop.wait(kj::mv(promise));
  checkTestMessage(received->getRoot<TestAllTypes>());
}

TEST_F(SerializeAsyncTest, ParseAsyncOddSegmentCount) {
  kj::UnixEventLoop loop;

  auto input = kj::AsyncInputStream::wrapFd(fds[0]);
  kj::FdOutputStream rawOutput(fds[1]);
  FragmentingOutputStream output(rawOutput);

  TestMessageBuilder message(7);
  initTestMessage(message.getRoot<TestAllTypes>());

  auto promise = loop.evalLater([&]() {
    return readMessage(*input);
  });

  kj::Thread thread([&]() {
    writeMessage(output, message);
  });

  auto received = loop.wait(kj::mv(promise));
  checkTestMessage(received->getRoot<TestAllTypes>());
}

TEST_F(SerializeAsyncTest, ParseAsyncEvenSegmentCount) {
  kj::UnixEventLoop loop;

  auto input = kj::AsyncInputStream::wrapFd(fds[0]);
  kj::FdOutputStream rawOutput(fds[1]);
  FragmentingOutputStream output(rawOutput);

  TestMessageBuilder message(10);
  initTestMessage(message.getRoot<TestAllTypes>());

  auto promise = loop.evalLater([&]() {
    return readMessage(*input);
  });

  kj::Thread thread([&]() {
    writeMessage(output, message);
  });

  auto received = loop.wait(kj::mv(promise));
  checkTestMessage(received->getRoot<TestAllTypes>());
}

TEST_F(SerializeAsyncTest, WriteAsync) {
  kj::UnixEventLoop loop;

  auto output = kj::AsyncOutputStream::wrapFd(fds[1]);

  TestMessageBuilder message(1);
  auto root = message.getRoot<TestAllTypes>();
  auto list = root.initStructList(16);
  for (auto element: list) {
    initTestMessage(element);
  }

  kj::Thread thread([&]() {
    StreamFdMessageReader reader(fds[0]);
    auto listReader = reader.getRoot<TestAllTypes>().getStructList();
    EXPECT_EQ(list.size(), listReader.size());
    for (auto element: listReader) {
      checkTestMessage(element);
    }
  });

  loop.wait(loop.evalLater([&]() {
    return writeMessage(*output, message);
  }));
}

TEST_F(SerializeAsyncTest, WriteAsyncOddSegmentCount) {
  kj::UnixEventLoop loop;

  auto output = kj::AsyncOutputStream::wrapFd(fds[1]);

  TestMessageBuilder message(7);
  auto root = message.getRoot<TestAllTypes>();
  auto list = root.initStructList(16);
  for (auto element: list) {
    initTestMessage(element);
  }

  kj::Thread thread([&]() {
    StreamFdMessageReader reader(fds[0]);
    auto listReader = reader.getRoot<TestAllTypes>().getStructList();
    EXPECT_EQ(list.size(), listReader.size());
    for (auto element: listReader) {
      checkTestMessage(element);
    }
  });

  loop.wait(loop.evalLater([&]() {
    return writeMessage(*output, message);
  }));
}

TEST_F(SerializeAsyncTest, WriteAsyncEvenSegmentCount) {
  kj::UnixEventLoop loop;

  auto output = kj::AsyncOutputStream::wrapFd(fds[1]);

  TestMessageBuilder message(10);
  auto root = message.getRoot<TestAllTypes>();
  auto list = root.initStructList(16);
  for (auto element: list) {
    initTestMessage(element);
  }

  kj::Thread thread([&]() {
    StreamFdMessageReader reader(fds[0]);
    auto listReader = reader.getRoot<TestAllTypes>().getStructList();
    EXPECT_EQ(list.size(), listReader.size());
    for (auto element: listReader) {
      checkTestMessage(element);
    }
  });

  loop.wait(loop.evalLater([&]() {
    return writeMessage(*output, message);
  }));
}

}  // namespace
}  // namespace _ (private)
}  // namespace capnp
<commit_msg>Work around OSX.<commit_after>// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "serialize-async.h"
#include "serialize.h"
#include <kj/debug.h>
#include <kj/thread.h>
#include <kj/async-unix.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "test-util.h"
#include <gtest/gtest.h>

namespace capnp {
namespace _ {  // private
namespace {

class FragmentingOutputStream: public kj::OutputStream {
public:
  FragmentingOutputStream(kj::OutputStream& inner): inner(inner) {}

  void write(const void* buffer, size_t size) override {
    while (size > 0) {
      size_t n = rand() % size + 1;
      inner.write(buffer, n);
      usleep(5);
      buffer = reinterpret_cast<const byte*>(buffer) + n;
      size -= n;
    }
  }

private:
  kj::OutputStream& inner;
};

class TestMessageBuilder: public MallocMessageBuilder {
  // A MessageBuilder that tries to allocate an exact number of total segments, by allocating
  // minimum-size segments until it reaches the number, then allocating one large segment to
  // finish.

public:
  explicit TestMessageBuilder(uint desiredSegmentCount)
      : MallocMessageBuilder(0, AllocationStrategy::FIXED_SIZE),
        desiredSegmentCount(desiredSegmentCount) {}
  ~TestMessageBuilder() {
    EXPECT_EQ(0u, desiredSegmentCount);
  }

  kj::ArrayPtr<word> allocateSegment(uint minimumSize) override {
    if (desiredSegmentCount <= 1) {
      if (desiredSegmentCount < 1) {
        ADD_FAILURE() << "Allocated more segments than desired.";
      } else {
        --desiredSegmentCount;
      }
      return MallocMessageBuilder::allocateSegment(8192);
    } else {
      --desiredSegmentCount;
      return MallocMessageBuilder::allocateSegment(minimumSize);
    }
  }

private:
  uint desiredSegmentCount;
};

class SerializeAsyncTest: public testing::Test {
protected:
  int fds[2];

  SerializeAsyncTest() {
    // Use a socketpair rather than a pipe so that we can set the buffer size extremely small.
    KJ_SYSCALL(socketpair(AF_UNIX, SOCK_STREAM, 0, fds));

    KJ_SYSCALL(shutdown(fds[0], SHUT_WR));
    // Note:  OSX reports ENOTCONN if we also try to shutdown(fds[1], SHUT_RD).

    // Request that the buffer size be as small as possible, to force the event loop to kick in.
    uint zero = 0;
    KJ_SYSCALL(setsockopt(fds[0], SOL_SOCKET, SO_RCVBUF, &zero, sizeof(zero)));
    KJ_SYSCALL(setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, &zero, sizeof(zero)));
  }
  ~SerializeAsyncTest() {
    close(fds[0]);
    close(fds[1]);
  }
};

TEST_F(SerializeAsyncTest, ParseAsync) {
  kj::UnixEventLoop loop;

  auto input = kj::AsyncInputStream::wrapFd(fds[0]);
  kj::FdOutputStream rawOutput(fds[1]);
  FragmentingOutputStream output(rawOutput);

  TestMessageBuilder message(1);
  initTestMessage(message.getRoot<TestAllTypes>());

  auto promise = loop.evalLater([&]() {
    return readMessage(*input);
  });

  kj::Thread thread([&]() {
    writeMessage(output, message);
  });

  auto received = loop.wait(kj::mv(promise));
  checkTestMessage(received->getRoot<TestAllTypes>());
}

TEST_F(SerializeAsyncTest, ParseAsyncOddSegmentCount) {
  kj::UnixEventLoop loop;

  auto input = kj::AsyncInputStream::wrapFd(fds[0]);
  kj::FdOutputStream rawOutput(fds[1]);
  FragmentingOutputStream output(rawOutput);

  TestMessageBuilder message(7);
  initTestMessage(message.getRoot<TestAllTypes>());

  auto promise = loop.evalLater([&]() {
    return readMessage(*input);
  });

  kj::Thread thread([&]() {
    writeMessage(output, message);
  });

  auto received = loop.wait(kj::mv(promise));
  checkTestMessage(received->getRoot<TestAllTypes>());
}

TEST_F(SerializeAsyncTest, ParseAsyncEvenSegmentCount) {
  kj::UnixEventLoop loop;

  auto input = kj::AsyncInputStream::wrapFd(fds[0]);
  kj::FdOutputStream rawOutput(fds[1]);
  FragmentingOutputStream output(rawOutput);

  TestMessageBuilder message(10);
  initTestMessage(message.getRoot<TestAllTypes>());

  auto promise = loop.evalLater([&]() {
    return readMessage(*input);
  });

  kj::Thread thread([&]() {
    writeMessage(output, message);
  });

  auto received = loop.wait(kj::mv(promise));
  checkTestMessage(received->getRoot<TestAllTypes>());
}

TEST_F(SerializeAsyncTest, WriteAsync) {
  kj::UnixEventLoop loop;

  auto output = kj::AsyncOutputStream::wrapFd(fds[1]);

  TestMessageBuilder message(1);
  auto root = message.getRoot<TestAllTypes>();
  auto list = root.initStructList(16);
  for (auto element: list) {
    initTestMessage(element);
  }

  kj::Thread thread([&]() {
    StreamFdMessageReader reader(fds[0]);
    auto listReader = reader.getRoot<TestAllTypes>().getStructList();
    EXPECT_EQ(list.size(), listReader.size());
    for (auto element: listReader) {
      checkTestMessage(element);
    }
  });

  loop.wait(loop.evalLater([&]() {
    return writeMessage(*output, message);
  }));
}

TEST_F(SerializeAsyncTest, WriteAsyncOddSegmentCount) {
  kj::UnixEventLoop loop;

  auto output = kj::AsyncOutputStream::wrapFd(fds[1]);

  TestMessageBuilder message(7);
  auto root = message.getRoot<TestAllTypes>();
  auto list = root.initStructList(16);
  for (auto element: list) {
    initTestMessage(element);
  }

  kj::Thread thread([&]() {
    StreamFdMessageReader reader(fds[0]);
    auto listReader = reader.getRoot<TestAllTypes>().getStructList();
    EXPECT_EQ(list.size(), listReader.size());
    for (auto element: listReader) {
      checkTestMessage(element);
    }
  });

  loop.wait(loop.evalLater([&]() {
    return writeMessage(*output, message);
  }));
}

TEST_F(SerializeAsyncTest, WriteAsyncEvenSegmentCount) {
  kj::UnixEventLoop loop;

  auto output = kj::AsyncOutputStream::wrapFd(fds[1]);

  TestMessageBuilder message(10);
  auto root = message.getRoot<TestAllTypes>();
  auto list = root.initStructList(16);
  for (auto element: list) {
    initTestMessage(element);
  }

  kj::Thread thread([&]() {
    StreamFdMessageReader reader(fds[0]);
    auto listReader = reader.getRoot<TestAllTypes>().getStructList();
    EXPECT_EQ(list.size(), listReader.size());
    for (auto element: listReader) {
      checkTestMessage(element);
    }
  });

  loop.wait(loop.evalLater([&]() {
    return writeMessage(*output, message);
  }));
}

}  // namespace
}  // namespace _ (private)
}  // namespace capnp
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- *
 *                   OpenSim:  InverseKinematicsSolver.cpp                    *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2016 Stanford University and the Authors                *
 * Author(s): Ajay Seth                                                       *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */

#include "InverseKinematicsSolver.h"
#include "MarkersReference.h"
#include "Model/Model.h"
#include "Model/MarkerSet.h"

#include "simbody/internal/AssemblyCondition_Markers.h"

using namespace std;
using namespace SimTK;

namespace OpenSim {

//______________________________________________________________________________
/*
 * An implementation of the InverseKinematicsSolver
 *
 * @param model to assemble
 */
InverseKinematicsSolver::InverseKinematicsSolver(const Model &model, MarkersReference &markersReference,
                            SimTK::Array_<CoordinateReference> &coordinateReferences,
                            double constraintWeight) : AssemblySolver(model, coordinateReferences, constraintWeight),
                            _markersReference(markersReference)
{
    setAuthors("Ajay Seth");

    // Do some consistency checking for markers
    const MarkerSet &modelMarkerSet = getModel().getMarkerSet();

    if(modelMarkerSet.getSize() < 1){
        std::cout << "InverseKinematicsSolver: Model has no markers!"  << std::endl;
        throw Exception("InverseKinematicsSolver: Model has no markers!");
    }

    const SimTK::Array_<std::string> &markerNames = _markersReference.getNames(); // size and content as in trc file

    if(markerNames.size() < 1){
        std::cout << "InverseKinematicsSolver: No markers available from data provided."  << std::endl;
        throw Exception("InverseKinematicsSolver: No markers available from data provided.");
    }
    int index=0, cnt=0;
    for(unsigned int i=0; i < markerNames.size(); i++) {
        // Check if we have this marker in the model, else ignore it
        index = modelMarkerSet.getIndex(markerNames[i], index);
        if(index >= 0) //found corresponding model
            cnt++;
    }

    if(cnt < 1){
        std::cout <<"InverseKinematicsSolver: Marker data does not correspond to any model markers." << std::endl;
        throw Exception("InverseKinematicsSolver: Marker data does not correspond to any model markers.");
    }
    if(cnt < 4)
        cout << "WARNING: InverseKinematicsSolver found only " << cnt << " markers to track." << endl;

}

/* Change the weighting of a marker to take affect when assemble or track is called next.
   Update a marker's weight by name. */
void InverseKinematicsSolver::updateMarkerWeight(const std::string &markerName, double value)
{
    const Array_<std::string> &names = _markersReference.getNames();
    SimTK::Array_<const std::string>::iterator p = std::find(names.begin(), names.end(), markerName);
    int index = (int)std::distance(names.begin(), p);
    updateMarkerWeight(index, value);
}

/* Update a marker's weight by its index. */
void InverseKinematicsSolver::updateMarkerWeight(int markerIndex, double value)
{
    if(markerIndex >=0 && markerIndex < _markersReference.updMarkerWeightSet().getSize()){
        _markersReference.updMarkerWeightSet()[markerIndex].setWeight(value);
        _markerAssemblyCondition->changeMarkerWeight(SimTK::Markers::MarkerIx(markerIndex), value);
    }
    else
        throw Exception("InverseKinematicsSolver::updateMarkerWeight: invalid markerIndex.");
}

/* Update all markers weights by order in the markersReference passed in to
   construct the solver. */
void InverseKinematicsSolver::updateMarkerWeights(const SimTK::Array_<double> &weights)
{
    if(static_cast<unsigned>(_markersReference.updMarkerWeightSet().getSize())
       == weights.size()){
        for(unsigned int i=0; i<weights.size(); i++){
            _markersReference.updMarkerWeightSet()[i].setWeight(weights[i]);
            _markerAssemblyCondition->changeMarkerWeight(SimTK::Markers::MarkerIx(i), weights[i]);
        }
    }
    else
        throw Exception("InverseKinematicsSolver::updateMarkerWeights: invalid size of weights.");
}

/* Compute and return the spatial location of a marker in ground. */
SimTK::Vec3 InverseKinematicsSolver::computeCurrentMarkerLocation(const std::string &markerName)
{
    const Array_<std::string> &names = _markersReference.getNames();
    SimTK::Array_<const std::string>::iterator p = std::find(names.begin(), names.end(), markerName);
    int index = (int)std::distance(names.begin(), p);
    return computeCurrentMarkerLocation(index);
}

SimTK::Vec3 InverseKinematicsSolver::computeCurrentMarkerLocation(int markerIndex)
{
    if(markerIndex >=0 && markerIndex < _markerAssemblyCondition->getNumMarkers()){
        return _markerAssemblyCondition->findCurrentMarkerLocation(SimTK::Markers::MarkerIx(markerIndex));
    }
    else
        throw Exception("InverseKinematicsSolver::computeCurrentMarkerLocation: invalid markerIndex.");
}

/* Compute and return the spatial locations of all markers in ground. */
void InverseKinematicsSolver::computeCurrentMarkerLocations(SimTK::Array_<SimTK::Vec3> &markerLocations)
{
    markerLocations.resize(_markerAssemblyCondition->getNumMarkers());
    for(unsigned int i=0; i<markerLocations.size(); i++)
        markerLocations[i] = _markerAssemblyCondition->findCurrentMarkerLocation(SimTK::Markers::MarkerIx(i));
}

/* Compute and return the distance error between model marker and observation. */
double InverseKinematicsSolver::computeCurrentMarkerError(const std::string &markerName)
{
    const Array_<std::string> &names = _markersReference.getNames();
    SimTK::Array_<const std::string>::iterator p = std::find(names.begin(), names.end(), markerName);
    int index = (int)std::distance(names.begin(), p);
    return computeCurrentMarkerError(index);
}

double InverseKinematicsSolver::computeCurrentMarkerError(int markerIndex)
{
    if(markerIndex >=0 && markerIndex < _markerAssemblyCondition->getNumMarkers()){
        return _markerAssemblyCondition->findCurrentMarkerError(SimTK::Markers::MarkerIx(markerIndex));
    }
    else
        throw Exception("InverseKinematicsSolver::computeCurrentMarkerError: invalid markerIndex.");
}

/* Compute and return the distance errors between all model markers and their observations. */
void InverseKinematicsSolver::computeCurrentMarkerErrors(SimTK::Array_<double> &markerErrors)
{
    markerErrors.resize(_markerAssemblyCondition->getNumMarkers());
    for(unsigned int i=0; i<markerErrors.size(); i++)
        markerErrors[i] = _markerAssemblyCondition->findCurrentMarkerError(SimTK::Markers::MarkerIx(i));
}

/* Compute and return the squared-distance error between model marker and observation. */
double InverseKinematicsSolver::computeCurrentSquaredMarkerError(const std::string &markerName)
{
    const Array_<std::string> &names = _markersReference.getNames();
    SimTK::Array_<const std::string>::iterator p = std::find(names.begin(), names.end(), markerName);
    int index = (int)std::distance(names.begin(), p);
    return computeCurrentSquaredMarkerError(index);
}

double InverseKinematicsSolver::computeCurrentSquaredMarkerError(int markerIndex)
{
    if(markerIndex >=0 && markerIndex < _markerAssemblyCondition->getNumMarkers()){
        return _markerAssemblyCondition->findCurrentMarkerErrorSquared(SimTK::Markers::MarkerIx(markerIndex));
    }
    else
        throw Exception("InverseKinematicsSolver::computeCurrentMarkerSquaredError: invalid markerIndex.");
}

/* Compute and return the distance errors between all model marker and observations. */
void InverseKinematicsSolver::computeCurrentSquaredMarkerErrors(SimTK::Array_<double> &markerErrors)
{
    markerErrors.resize(_markerAssemblyCondition->getNumMarkers());
    for(unsigned int i=0; i<markerErrors.size(); i++)
        markerErrors[i] = _markerAssemblyCondition->findCurrentMarkerErrorSquared(SimTK::Markers::MarkerIx(i));
}

/* Marker errors are reported in order different from tasks file or model, find name corresponding to passed in index  */
std::string InverseKinematicsSolver::getMarkerNameForIndex(int markerIndex) const
{
    return _markerAssemblyCondition->getMarkerName(SimTK::Markers::MarkerIx(markerIndex));
}

/* Internal method to convert the MarkerReferences into additional goals of the
    of the base assembly solver, that is going to do the assembly.  */
void InverseKinematicsSolver::setupGoals(SimTK::State &s)
{
    // Setup coordinates performed by the base class
    AssemblySolver::setupGoals(s);

    std::unique_ptr<SimTK::Markers> condOwner(new SimTK::Markers());
    _markerAssemblyCondition.reset(condOwner.get());

    // Setup markers goals
    // Get lists of all markers by names and corresponding weights from the MarkersReference
    const SimTK::Array_<SimTK::String> &markerNames = _markersReference.getNames();
    SimTK::Array_<double> markerWeights;
    _markersReference.getWeights(s, markerWeights);
    // get markers defined by the model
    const MarkerSet &modelMarkerSet = getModel().getMarkerSet();

    // get markers with specified tasks/weights
    const Set<MarkerWeight>& mwSet = _markersReference.updMarkerWeightSet();

    int index = -1;
    int wIndex = -1;
    SimTK::Transform X_BF;
    //Loop through all markers in the reference
    for(unsigned int i=0; i < markerNames.size(); ++i){
        // Check if we have this marker in the model, else ignore it
        index = modelMarkerSet.getIndex(markerNames[i], index);
        wIndex = mwSet.getIndex(markerNames[i],wIndex);
        if((index >= 0) && (wIndex >=0)){
            Marker &marker = modelMarkerSet[index];
            const SimTK::MobilizedBody& mobod =
                marker.getParentFrame().getMobilizedBody();

            X_BF = marker.getParentFrame().findTransformInBaseFrame();
            _markerAssemblyCondition->
                addMarker(marker.getName(), mobod, X_BF*marker.get_location(),
                          markerWeights[i]);
        }
    }

    // Add marker goal to the ik objective and transfer ownership of the
    // goal (AssemblyCondition) to Assembler
    updAssembler().adoptAssemblyGoal(condOwner.release());
    // lock-in the order that the observations (markers) are in and this cannot change from frame to frame
    // and we can use an array of just the data for updating
    _markerAssemblyCondition->defineObservationOrder(markerNames);

    updateGoals(s);
}

/* Internal method to update the time, reference values and/or their weights based
    on the state */
void InverseKinematicsSolver::updateGoals(const SimTK::State &s)
{
    // update coordinates performed by the base class
    AssemblySolver::updateGoals(s);

    // specify the (initial) observations to be matched
    _markersReference.getValues(s, _markerValues);
    _markerAssemblyCondition->moveAllObservations(_markerValues);
}

} // end of namespace OpenSim
<commit_msg>No longer involve the InverseKinematicsSolver update marker weights from the MarkersReference's marker_weights property. The implementation of the MarkersReference is no longer exposed and MarkersReference::getWeights() now correctly updates based on its internal changes and is sufficient.<commit_after>/* -------------------------------------------------------------------------- *
 *                   OpenSim:  InverseKinematicsSolver.cpp                    *
 * -------------------------------------------------------------------------- *
 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
 * See http://opensim.stanford.edu and the NOTICE file for more information.  *
 * OpenSim is developed at Stanford University and supported by the US        *
 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
 * through the Warrior Web program.                                           *
 *                                                                            *
 * Copyright (c) 2005-2016 Stanford University and the Authors                *
 * Author(s): Ajay Seth                                                       *
 *                                                                            *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License. You may obtain a  *
 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
 *                                                                            *
 * Unless required by applicable law or agreed to in writing, software        *
 * distributed under the License is distributed on an "AS IS" BASIS,          *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
 * See the License for the specific language governing permissions and        *
 * limitations under the License.                                             *
 * -------------------------------------------------------------------------- */

#include "InverseKinematicsSolver.h"
#include "MarkersReference.h"
#include "Model/Model.h"
#include "Model/MarkerSet.h"

#include "simbody/internal/AssemblyCondition_Markers.h"

using namespace std;
using namespace SimTK;

namespace OpenSim {

//______________________________________________________________________________
/*
 * An implementation of the InverseKinematicsSolver
 *
 * @param model to assemble
 */
InverseKinematicsSolver::InverseKinematicsSolver(const Model &model, MarkersReference &markersReference,
                            SimTK::Array_<CoordinateReference> &coordinateReferences,
                            double constraintWeight) : AssemblySolver(model, coordinateReferences, constraintWeight),
                            _markersReference(markersReference)
{
    setAuthors("Ajay Seth");

    // Do some consistency checking for markers
    const MarkerSet &modelMarkerSet = getModel().getMarkerSet();

    if(modelMarkerSet.getSize() < 1){
        std::cout << "InverseKinematicsSolver: Model has no markers!"  << std::endl;
        throw Exception("InverseKinematicsSolver: Model has no markers!");
    }

    const SimTK::Array_<std::string> &markerNames = _markersReference.getNames(); // size and content as in trc file

    if(markerNames.size() < 1){
        std::cout << "InverseKinematicsSolver: No markers available from data provided."  << std::endl;
        throw Exception("InverseKinematicsSolver: No markers available from data provided.");
    }
    int index=0, cnt=0;
    for(unsigned int i=0; i < markerNames.size(); i++) {
        // Check if we have this marker in the model, else ignore it
        index = modelMarkerSet.getIndex(markerNames[i], index);
        if(index >= 0) //found corresponding model
            cnt++;
    }

    if(cnt < 1){
        std::cout <<"InverseKinematicsSolver: Marker data does not correspond to any model markers." << std::endl;
        throw Exception("InverseKinematicsSolver: Marker data does not correspond to any model markers.");
    }
    if(cnt < 4)
        cout << "WARNING: InverseKinematicsSolver found only " << cnt << " markers to track." << endl;

}

/* Change the weighting of a marker to take affect when assemble or track is called next.
   Update a marker's weight by name. */
void InverseKinematicsSolver::updateMarkerWeight(const std::string &markerName, double value)
{
    const Array_<std::string> &names = _markersReference.getNames();
    SimTK::Array_<const std::string>::iterator p = std::find(names.begin(), names.end(), markerName);
    int index = (int)std::distance(names.begin(), p);
    updateMarkerWeight(index, value);
}

/* Update a marker's weight by its index. */
void InverseKinematicsSolver::updateMarkerWeight(int markerIndex, double value)
{
    if(markerIndex >=0 && markerIndex < _markersReference.updMarkerWeightSet().getSize()){
        _markersReference.updMarkerWeightSet()[markerIndex].setWeight(value);
        _markerAssemblyCondition->changeMarkerWeight(SimTK::Markers::MarkerIx(markerIndex), value);
    }
    else
        throw Exception("InverseKinematicsSolver::updateMarkerWeight: invalid markerIndex.");
}

/* Update all markers weights by order in the markersReference passed in to
   construct the solver. */
void InverseKinematicsSolver::updateMarkerWeights(const SimTK::Array_<double> &weights)
{
    if(static_cast<unsigned>(_markersReference.updMarkerWeightSet().getSize())
       == weights.size()){
        for(unsigned int i=0; i<weights.size(); i++){
            _markersReference.updMarkerWeightSet()[i].setWeight(weights[i]);
            _markerAssemblyCondition->changeMarkerWeight(SimTK::Markers::MarkerIx(i), weights[i]);
        }
    }
    else
        throw Exception("InverseKinematicsSolver::updateMarkerWeights: invalid size of weights.");
}

/* Compute and return the spatial location of a marker in ground. */
SimTK::Vec3 InverseKinematicsSolver::computeCurrentMarkerLocation(const std::string &markerName)
{
    const Array_<std::string> &names = _markersReference.getNames();
    SimTK::Array_<const std::string>::iterator p = std::find(names.begin(), names.end(), markerName);
    int index = (int)std::distance(names.begin(), p);
    return computeCurrentMarkerLocation(index);
}

SimTK::Vec3 InverseKinematicsSolver::computeCurrentMarkerLocation(int markerIndex)
{
    if(markerIndex >=0 && markerIndex < _markerAssemblyCondition->getNumMarkers()){
        return _markerAssemblyCondition->findCurrentMarkerLocation(SimTK::Markers::MarkerIx(markerIndex));
    }
    else
        throw Exception("InverseKinematicsSolver::computeCurrentMarkerLocation: invalid markerIndex.");
}

/* Compute and return the spatial locations of all markers in ground. */
void InverseKinematicsSolver::computeCurrentMarkerLocations(SimTK::Array_<SimTK::Vec3> &markerLocations)
{
    markerLocations.resize(_markerAssemblyCondition->getNumMarkers());
    for(unsigned int i=0; i<markerLocations.size(); i++)
        markerLocations[i] = _markerAssemblyCondition->findCurrentMarkerLocation(SimTK::Markers::MarkerIx(i));
}

/* Compute and return the distance error between model marker and observation. */
double InverseKinematicsSolver::computeCurrentMarkerError(const std::string &markerName)
{
    const Array_<std::string> &names = _markersReference.getNames();
    SimTK::Array_<const std::string>::iterator p = std::find(names.begin(), names.end(), markerName);
    int index = (int)std::distance(names.begin(), p);
    return computeCurrentMarkerError(index);
}

double InverseKinematicsSolver::computeCurrentMarkerError(int markerIndex)
{
    if(markerIndex >=0 && markerIndex < _markerAssemblyCondition->getNumMarkers()){
        return _markerAssemblyCondition->findCurrentMarkerError(SimTK::Markers::MarkerIx(markerIndex));
    }
    else
        throw Exception("InverseKinematicsSolver::computeCurrentMarkerError: invalid markerIndex.");
}

/* Compute and return the distance errors between all model markers and their observations. */
void InverseKinematicsSolver::computeCurrentMarkerErrors(SimTK::Array_<double> &markerErrors)
{
    markerErrors.resize(_markerAssemblyCondition->getNumMarkers());
    for(unsigned int i=0; i<markerErrors.size(); i++)
        markerErrors[i] = _markerAssemblyCondition->findCurrentMarkerError(SimTK::Markers::MarkerIx(i));
}

/* Compute and return the squared-distance error between model marker and observation. */
double InverseKinematicsSolver::computeCurrentSquaredMarkerError(const std::string &markerName)
{
    const Array_<std::string> &names = _markersReference.getNames();
    SimTK::Array_<const std::string>::iterator p = std::find(names.begin(), names.end(), markerName);
    int index = (int)std::distance(names.begin(), p);
    return computeCurrentSquaredMarkerError(index);
}

double InverseKinematicsSolver::computeCurrentSquaredMarkerError(int markerIndex)
{
    if(markerIndex >=0 && markerIndex < _markerAssemblyCondition->getNumMarkers()){
        return _markerAssemblyCondition->findCurrentMarkerErrorSquared(SimTK::Markers::MarkerIx(markerIndex));
    }
    else
        throw Exception("InverseKinematicsSolver::computeCurrentMarkerSquaredError: invalid markerIndex.");
}

/* Compute and return the distance errors between all model marker and observations. */
void InverseKinematicsSolver::computeCurrentSquaredMarkerErrors(SimTK::Array_<double> &markerErrors)
{
    markerErrors.resize(_markerAssemblyCondition->getNumMarkers());
    for(unsigned int i=0; i<markerErrors.size(); i++)
        markerErrors[i] = _markerAssemblyCondition->findCurrentMarkerErrorSquared(SimTK::Markers::MarkerIx(i));
}

/* Marker errors are reported in order different from tasks file or model, find name corresponding to passed in index  */
std::string InverseKinematicsSolver::getMarkerNameForIndex(int markerIndex) const
{
    return _markerAssemblyCondition->getMarkerName(SimTK::Markers::MarkerIx(markerIndex));
}

/* Internal method to convert the MarkerReferences into additional goals of the
    of the base assembly solver, that is going to do the assembly.  */
void InverseKinematicsSolver::setupGoals(SimTK::State &s)
{
    // Setup coordinates performed by the base class
    AssemblySolver::setupGoals(s);

    std::unique_ptr<SimTK::Markers> condOwner(new SimTK::Markers());
    _markerAssemblyCondition.reset(condOwner.get());

    // Setup markers goals
    // Get lists of all markers by names and corresponding weights from the MarkersReference
    const SimTK::Array_<SimTK::String> &markerNames = _markersReference.getNames();
    SimTK::Array_<double> markerWeights;
    _markersReference.getWeights(s, markerWeights);
    // get markers defined by the model
    const MarkerSet &modelMarkerSet = getModel().getMarkerSet();

    int index = -1;
    SimTK::Transform X_BF;
    //Loop through all markers in the reference
    for(unsigned int i=0; i < markerNames.size(); ++i){
        // Check if we have this marker in the model, else ignore it
        index = modelMarkerSet.getIndex(markerNames[i], index);
        if(index >= 0) {
            Marker &marker = modelMarkerSet[index];
            const SimTK::MobilizedBody& mobod =
                marker.getParentFrame().getMobilizedBody();

            X_BF = marker.getParentFrame().findTransformInBaseFrame();
            _markerAssemblyCondition->
                addMarker(marker.getName(), mobod, X_BF*marker.get_location(),
                          markerWeights[i]);
        }
    }

    // Add marker goal to the ik objective and transfer ownership of the
    // goal (AssemblyCondition) to Assembler
    updAssembler().adoptAssemblyGoal(condOwner.release());
    // lock-in the order that the observations (markers) are in and this cannot change from frame to frame
    // and we can use an array of just the data for updating
    _markerAssemblyCondition->defineObservationOrder(markerNames);

    updateGoals(s);
}

/* Internal method to update the time, reference values and/or their weights based
    on the state */
void InverseKinematicsSolver::updateGoals(const SimTK::State &s)
{
    // update coordinates performed by the base class
    AssemblySolver::updateGoals(s);

    // specify the (initial) observations to be matched
    _markersReference.getValues(s, _markerValues);
    _markerAssemblyCondition->moveAllObservations(_markerValues);
}

} // end of namespace OpenSim
<|endoftext|>


<commit_before>//===-- RecordLayout.cpp - Layout information for a struct/union -*- C++ -*-==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file defines the RecordLayout interface.
//
//===----------------------------------------------------------------------===//

#include "clang/AST/ASTContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/TargetInfo.h"

using namespace clang;

void ASTRecordLayout::Destroy(ASTContext &Ctx) {
  if (FieldOffsets)
    Ctx.Deallocate(FieldOffsets);
  if (CXXInfo) {
    Ctx.Deallocate(CXXInfo);
    CXXInfo->~CXXRecordLayoutInfo();
  }
  this->~ASTRecordLayout();
  Ctx.Deallocate(this);
}

ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size,
                                 CharUnits alignment,
                                 CharUnits requiredAlignment,
                                 CharUnits datasize,
                                 const uint64_t *fieldoffsets,
                                 unsigned fieldcount)
  : Size(size), DataSize(datasize), Alignment(alignment),
    RequiredAlignment(requiredAlignment), FieldOffsets(nullptr),
    FieldCount(fieldcount), CXXInfo(nullptr) {
  if (FieldCount > 0)  {
    FieldOffsets = new (Ctx) uint64_t[FieldCount];
    memcpy(FieldOffsets, fieldoffsets, FieldCount * sizeof(*FieldOffsets));
  }
}

// Constructor for C++ records.
ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
                                 CharUnits size, CharUnits alignment,
                                 CharUnits requiredAlignment,
                                 bool hasOwnVFPtr, bool hasExtendableVFPtr,
                                 CharUnits vbptroffset,
                                 CharUnits datasize,
                                 const uint64_t *fieldoffsets,
                                 unsigned fieldcount,
                                 CharUnits nonvirtualsize,
                                 CharUnits nonvirtualalignment,
                                 CharUnits SizeOfLargestEmptySubobject,
                                 const CXXRecordDecl *PrimaryBase,
                                 bool IsPrimaryBaseVirtual,
                                 const CXXRecordDecl *BaseSharingVBPtr,
                                 bool HasZeroSizedSubObject,
                                 bool LeadsWithZeroSizedBase,
                                 const BaseOffsetsMapTy& BaseOffsets,
                                 const VBaseOffsetsMapTy& VBaseOffsets)
  : Size(size), DataSize(datasize), Alignment(alignment),
    RequiredAlignment(requiredAlignment), FieldOffsets(nullptr),
    FieldCount(fieldcount), CXXInfo(new (Ctx) CXXRecordLayoutInfo)
{
  if (FieldCount > 0)  {
    FieldOffsets = new (Ctx) uint64_t[FieldCount];
    memcpy(FieldOffsets, fieldoffsets, FieldCount * sizeof(*FieldOffsets));
  }

  CXXInfo->PrimaryBase.setPointer(PrimaryBase);
  CXXInfo->PrimaryBase.setInt(IsPrimaryBaseVirtual);
  CXXInfo->NonVirtualSize = nonvirtualsize;
  CXXInfo->NonVirtualAlignment = nonvirtualalignment;
  CXXInfo->SizeOfLargestEmptySubobject = SizeOfLargestEmptySubobject;
  CXXInfo->BaseOffsets = BaseOffsets;
  CXXInfo->VBaseOffsets = VBaseOffsets;
  CXXInfo->HasOwnVFPtr = hasOwnVFPtr;
  CXXInfo->VBPtrOffset = vbptroffset;
  CXXInfo->HasExtendableVFPtr = hasExtendableVFPtr;
  CXXInfo->BaseSharingVBPtr = BaseSharingVBPtr;
  CXXInfo->HasZeroSizedSubObject = HasZeroSizedSubObject;
  CXXInfo->LeadsWithZeroSizedBase = LeadsWithZeroSizedBase;

#ifndef NDEBUG
    if (const CXXRecordDecl *PrimaryBase = getPrimaryBase()) {
      if (isPrimaryBaseVirtual()) {
        if (Ctx.getTargetInfo().getCXXABI().hasPrimaryVBases()) {
          assert(getVBaseClassOffset(PrimaryBase).isZero() &&
                 "Primary virtual base must be at offset 0!");
        }
      } else {
        assert(getBaseClassOffset(PrimaryBase).isZero() &&
               "Primary base must be at offset 0!");
      }
    }
#endif
}
<commit_msg>CXXInfo memory should be released after calling the destructor instead of before. The wrong order had no effect since Deallocate()  does nothing right now, but we may replace allocator in the future.<commit_after>//===-- RecordLayout.cpp - Layout information for a struct/union -*- C++ -*-==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file defines the RecordLayout interface.
//
//===----------------------------------------------------------------------===//

#include "clang/AST/ASTContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/TargetInfo.h"

using namespace clang;

void ASTRecordLayout::Destroy(ASTContext &Ctx) {
  if (FieldOffsets)
    Ctx.Deallocate(FieldOffsets);
  if (CXXInfo) {
    CXXInfo->~CXXRecordLayoutInfo();
    Ctx.Deallocate(CXXInfo);
  }
  this->~ASTRecordLayout();
  Ctx.Deallocate(this);
}

ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx, CharUnits size,
                                 CharUnits alignment,
                                 CharUnits requiredAlignment,
                                 CharUnits datasize,
                                 const uint64_t *fieldoffsets,
                                 unsigned fieldcount)
  : Size(size), DataSize(datasize), Alignment(alignment),
    RequiredAlignment(requiredAlignment), FieldOffsets(nullptr),
    FieldCount(fieldcount), CXXInfo(nullptr) {
  if (FieldCount > 0)  {
    FieldOffsets = new (Ctx) uint64_t[FieldCount];
    memcpy(FieldOffsets, fieldoffsets, FieldCount * sizeof(*FieldOffsets));
  }
}

// Constructor for C++ records.
ASTRecordLayout::ASTRecordLayout(const ASTContext &Ctx,
                                 CharUnits size, CharUnits alignment,
                                 CharUnits requiredAlignment,
                                 bool hasOwnVFPtr, bool hasExtendableVFPtr,
                                 CharUnits vbptroffset,
                                 CharUnits datasize,
                                 const uint64_t *fieldoffsets,
                                 unsigned fieldcount,
                                 CharUnits nonvirtualsize,
                                 CharUnits nonvirtualalignment,
                                 CharUnits SizeOfLargestEmptySubobject,
                                 const CXXRecordDecl *PrimaryBase,
                                 bool IsPrimaryBaseVirtual,
                                 const CXXRecordDecl *BaseSharingVBPtr,
                                 bool HasZeroSizedSubObject,
                                 bool LeadsWithZeroSizedBase,
                                 const BaseOffsetsMapTy& BaseOffsets,
                                 const VBaseOffsetsMapTy& VBaseOffsets)
  : Size(size), DataSize(datasize), Alignment(alignment),
    RequiredAlignment(requiredAlignment), FieldOffsets(nullptr),
    FieldCount(fieldcount), CXXInfo(new (Ctx) CXXRecordLayoutInfo)
{
  if (FieldCount > 0)  {
    FieldOffsets = new (Ctx) uint64_t[FieldCount];
    memcpy(FieldOffsets, fieldoffsets, FieldCount * sizeof(*FieldOffsets));
  }

  CXXInfo->PrimaryBase.setPointer(PrimaryBase);
  CXXInfo->PrimaryBase.setInt(IsPrimaryBaseVirtual);
  CXXInfo->NonVirtualSize = nonvirtualsize;
  CXXInfo->NonVirtualAlignment = nonvirtualalignment;
  CXXInfo->SizeOfLargestEmptySubobject = SizeOfLargestEmptySubobject;
  CXXInfo->BaseOffsets = BaseOffsets;
  CXXInfo->VBaseOffsets = VBaseOffsets;
  CXXInfo->HasOwnVFPtr = hasOwnVFPtr;
  CXXInfo->VBPtrOffset = vbptroffset;
  CXXInfo->HasExtendableVFPtr = hasExtendableVFPtr;
  CXXInfo->BaseSharingVBPtr = BaseSharingVBPtr;
  CXXInfo->HasZeroSizedSubObject = HasZeroSizedSubObject;
  CXXInfo->LeadsWithZeroSizedBase = LeadsWithZeroSizedBase;

#ifndef NDEBUG
    if (const CXXRecordDecl *PrimaryBase = getPrimaryBase()) {
      if (isPrimaryBaseVirtual()) {
        if (Ctx.getTargetInfo().getCXXABI().hasPrimaryVBases()) {
          assert(getVBaseClassOffset(PrimaryBase).isZero() &&
                 "Primary virtual base must be at offset 0!");
        }
      } else {
        assert(getBaseClassOffset(PrimaryBase).isZero() &&
               "Primary base must be at offset 0!");
      }
    }
#endif
}
<|endoftext|>


<commit_before>// Copyright 2013 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/memory/scoped_ptr.h"
#include "base/values.h"
#include "content/child/indexed_db/indexed_db_dispatcher.h"
#include "content/child/indexed_db/indexed_db_key_builders.h"
#include "content/child/indexed_db/webidbcursor_impl.h"
#include "content/child/thread_safe_sender.h"
#include "content/common/indexed_db/indexed_db_key.h"
#include "ipc/ipc_sync_message_filter.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebData.h"
#include "third_party/WebKit/public/platform/WebIDBCallbacks.h"

using blink::WebBlobInfo;
using blink::WebData;
using blink::WebIDBCallbacks;
using blink::WebIDBDatabase;
using blink::WebIDBKey;
using blink::WebIDBKeyTypeNumber;
using blink::WebVector;

namespace content {

namespace {

class MockDispatcher : public IndexedDBDispatcher {
 public:
  explicit MockDispatcher(ThreadSafeSender* thread_safe_sender)
      : IndexedDBDispatcher(thread_safe_sender),
        prefetch_calls_(0),
        last_prefetch_count_(0),
        reset_calls_(0),
        last_used_count_(0),
        advance_calls_(0),
        continue_calls_(0),
        destroyed_cursor_id_(0) {}

  virtual void RequestIDBCursorPrefetch(int n,
                                        WebIDBCallbacks* callbacks,
                                        int32 ipc_cursor_id) OVERRIDE {
    ++prefetch_calls_;
    last_prefetch_count_ = n;
    callbacks_.reset(callbacks);
  }

  virtual void RequestIDBCursorPrefetchReset(int used_prefetches,
                                             int unused_prefetches,
                                             int32 ipc_cursor_id) OVERRIDE {
    ++reset_calls_;
    last_used_count_ = used_prefetches;
  }

  virtual void RequestIDBCursorAdvance(unsigned long count,
                                       WebIDBCallbacks* callbacks,
                                       int32 ipc_cursor_id,
                                       int64 transaction_id) OVERRIDE {
    ++advance_calls_;
    callbacks_.reset(callbacks);
  }

  virtual void RequestIDBCursorContinue(const IndexedDBKey& key,
                                        const IndexedDBKey& primary_key,
                                        WebIDBCallbacks* callbacks,
                                        int32 ipc_cursor_id,
                                        int64 transaction_id) OVERRIDE {
    ++continue_calls_;
    callbacks_.reset(callbacks);
  }

  virtual void CursorDestroyed(int32 ipc_cursor_id) OVERRIDE {
    destroyed_cursor_id_ = ipc_cursor_id;
  }

  int prefetch_calls() { return prefetch_calls_; }
  int last_prefetch_count() { return last_prefetch_count_; }
  int reset_calls() { return reset_calls_; }
  int last_used_count() { return last_used_count_; }
  int advance_calls() { return advance_calls_; }
  int continue_calls() { return continue_calls_; }
  int32 destroyed_cursor_id() { return destroyed_cursor_id_; }

 private:
  int prefetch_calls_;
  int last_prefetch_count_;
  int reset_calls_;
  int last_used_count_;
  int advance_calls_;
  int continue_calls_;
  int32 destroyed_cursor_id_;
  scoped_ptr<WebIDBCallbacks> callbacks_;
};

class MockContinueCallbacks : public WebIDBCallbacks {
 public:
  MockContinueCallbacks(IndexedDBKey* key = 0,
                        WebVector<WebBlobInfo>* webBlobInfo = 0)
      : key_(key), webBlobInfo_(webBlobInfo) {}

  virtual void onSuccess(const WebIDBKey& key,
                         const WebIDBKey& primaryKey,
                         const WebData& value,
                         const WebVector<WebBlobInfo>& webBlobInfo) OVERRIDE {
    if (key_)
      *key_ = IndexedDBKeyBuilder::Build(key);
    if (webBlobInfo_)
      *webBlobInfo_ = webBlobInfo;
  }

 private:
  IndexedDBKey* key_;
  WebVector<WebBlobInfo>* webBlobInfo_;
};

}  // namespace

class WebIDBCursorImplTest : public testing::Test {
 public:
  WebIDBCursorImplTest() {
    null_key_.assignNull();
    sync_message_filter_ = new IPC::SyncMessageFilter(NULL);
    thread_safe_sender_ = new ThreadSafeSender(
        base::MessageLoopProxy::current(), sync_message_filter_.get());
    dispatcher_ =
        make_scoped_ptr(new MockDispatcher(thread_safe_sender_.get()));
  }

 protected:
  WebIDBKey null_key_;
  scoped_refptr<ThreadSafeSender> thread_safe_sender_;
  scoped_ptr<MockDispatcher> dispatcher_;

 private:
  scoped_refptr<IPC::SyncMessageFilter> sync_message_filter_;

  DISALLOW_COPY_AND_ASSIGN(WebIDBCursorImplTest);
};

TEST_F(WebIDBCursorImplTest, PrefetchTest) {
  const int64 transaction_id = 1;
  {
    WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId,
                            transaction_id,
                            thread_safe_sender_.get());

    // Call continue() until prefetching should kick in.
    int continue_calls = 0;
    EXPECT_EQ(dispatcher_->continue_calls(), 0);
    for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
      cursor.continueFunction(null_key_, new MockContinueCallbacks());
      EXPECT_EQ(++continue_calls, dispatcher_->continue_calls());
      EXPECT_EQ(0, dispatcher_->prefetch_calls());
    }

    // Do enough repetitions to verify that the count grows each time,
    // but not so many that the maximum limit is hit.
    const int kPrefetchRepetitions = 5;

    int expected_key = 0;
    int last_prefetch_count = 0;
    for (int repetitions = 0; repetitions < kPrefetchRepetitions;
         ++repetitions) {
      // Initiate the prefetch
      cursor.continueFunction(null_key_, new MockContinueCallbacks());
      EXPECT_EQ(continue_calls, dispatcher_->continue_calls());
      EXPECT_EQ(repetitions + 1, dispatcher_->prefetch_calls());

      // Verify that the requested count has increased since last time.
      int prefetch_count = dispatcher_->last_prefetch_count();
      EXPECT_GT(prefetch_count, last_prefetch_count);
      last_prefetch_count = prefetch_count;

      // Fill the prefetch cache as requested.
      std::vector<IndexedDBKey> keys;
      std::vector<IndexedDBKey> primary_keys(prefetch_count);
      std::vector<WebData> values(prefetch_count);
      std::vector<WebVector<WebBlobInfo> > blob_info;
      for (int i = 0; i < prefetch_count; ++i) {
        keys.push_back(IndexedDBKey(expected_key + i, WebIDBKeyTypeNumber));
        blob_info.push_back(
            WebVector<WebBlobInfo>(static_cast<size_t>(expected_key + i)));
      }
      cursor.SetPrefetchData(keys, primary_keys, values, blob_info);

      // Note that the real dispatcher would call cursor->CachedContinue()
      // immediately after cursor->SetPrefetchData() to service the request
      // that initiated the prefetch.

      // Verify that the cache is used for subsequent continue() calls.
      for (int i = 0; i < prefetch_count; ++i) {
        IndexedDBKey key;
        WebVector<WebBlobInfo> web_blob_info;
        cursor.continueFunction(
            null_key_, new MockContinueCallbacks(&key, &web_blob_info));
        EXPECT_EQ(continue_calls, dispatcher_->continue_calls());
        EXPECT_EQ(repetitions + 1, dispatcher_->prefetch_calls());

        EXPECT_EQ(WebIDBKeyTypeNumber, key.type());
        EXPECT_EQ(expected_key, static_cast<int>(web_blob_info.size()));
        EXPECT_EQ(expected_key++, key.number());
      }
    }
  }

  EXPECT_EQ(dispatcher_->destroyed_cursor_id(),
            WebIDBCursorImpl::kInvalidCursorId);
}

TEST_F(WebIDBCursorImplTest, AdvancePrefetchTest) {
  const int64 transaction_id = 1;
  WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId,
                          transaction_id,
                          thread_safe_sender_.get());

  // Call continue() until prefetching should kick in.
  EXPECT_EQ(0, dispatcher_->continue_calls());
  for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
    cursor.continueFunction(null_key_, new MockContinueCallbacks());
  }
  EXPECT_EQ(0, dispatcher_->prefetch_calls());

  // Initiate the prefetch
  cursor.continueFunction(null_key_, new MockContinueCallbacks());

  EXPECT_EQ(1, dispatcher_->prefetch_calls());
  EXPECT_EQ(static_cast<int>(WebIDBCursorImpl::kPrefetchContinueThreshold),
            dispatcher_->continue_calls());
  EXPECT_EQ(0, dispatcher_->advance_calls());

  const int prefetch_count = dispatcher_->last_prefetch_count();

  // Fill the prefetch cache as requested.
  int expected_key = 0;
  std::vector<IndexedDBKey> keys;
  std::vector<IndexedDBKey> primary_keys(prefetch_count);
  std::vector<WebData> values(prefetch_count);
  std::vector<WebVector<WebBlobInfo> > blob_info;
  for (int i = 0; i < prefetch_count; ++i) {
    keys.push_back(IndexedDBKey(expected_key + i, WebIDBKeyTypeNumber));
    blob_info.push_back(
        WebVector<WebBlobInfo>(static_cast<size_t>(expected_key + i)));
  }
  cursor.SetPrefetchData(keys, primary_keys, values, blob_info);

  // Note that the real dispatcher would call cursor->CachedContinue()
  // immediately after cursor->SetPrefetchData() to service the request
  // that initiated the prefetch.

  // Need at least this many in the cache for the test steps.
  ASSERT_GE(prefetch_count, 5);

  // IDBCursor.continue()
  IndexedDBKey key;
  cursor.continueFunction(null_key_, new MockContinueCallbacks(&key));
  EXPECT_EQ(0, key.number());

  // IDBCursor.advance(1)
  cursor.advance(1, new MockContinueCallbacks(&key));
  EXPECT_EQ(1, key.number());

  // IDBCursor.continue()
  cursor.continueFunction(null_key_, new MockContinueCallbacks(&key));
  EXPECT_EQ(2, key.number());

  // IDBCursor.advance(2)
  cursor.advance(2, new MockContinueCallbacks(&key));
  EXPECT_EQ(4, key.number());

  EXPECT_EQ(0, dispatcher_->advance_calls());

  // IDBCursor.advance(lots) - beyond the fetched amount
  cursor.advance(WebIDBCursorImpl::kMaxPrefetchAmount,
                 new MockContinueCallbacks(&key));
  EXPECT_EQ(1, dispatcher_->advance_calls());
  EXPECT_EQ(1, dispatcher_->prefetch_calls());
  EXPECT_EQ(static_cast<int>(WebIDBCursorImpl::kPrefetchContinueThreshold),
            dispatcher_->continue_calls());
}

TEST_F(WebIDBCursorImplTest, PrefetchReset) {
  const int64 transaction_id = 1;
  WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId,
                          transaction_id,
                          thread_safe_sender_.get());

  // Call continue() until prefetching should kick in.
  int continue_calls = 0;
  EXPECT_EQ(dispatcher_->continue_calls(), 0);
  for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
    cursor.continueFunction(null_key_, new MockContinueCallbacks());
    EXPECT_EQ(++continue_calls, dispatcher_->continue_calls());
    EXPECT_EQ(0, dispatcher_->prefetch_calls());
  }

  // Initiate the prefetch
  cursor.continueFunction(null_key_, new MockContinueCallbacks());
  EXPECT_EQ(continue_calls, dispatcher_->continue_calls());
  EXPECT_EQ(1, dispatcher_->prefetch_calls());
  EXPECT_EQ(0, dispatcher_->reset_calls());

  // Now invalidate it
  cursor.ResetPrefetchCache();

  // No reset should have been sent since nothing has been received yet.
  EXPECT_EQ(0, dispatcher_->reset_calls());

  // Fill the prefetch cache as requested.
  int prefetch_count = dispatcher_->last_prefetch_count();
  std::vector<IndexedDBKey> keys(prefetch_count);
  std::vector<IndexedDBKey> primary_keys(prefetch_count);
  std::vector<WebData> values(prefetch_count);
  std::vector<WebVector<WebBlobInfo> > blob_info(prefetch_count);
  cursor.SetPrefetchData(keys, primary_keys, values, blob_info);

  // No reset should have been sent since prefetch data hasn't been used.
  EXPECT_EQ(0, dispatcher_->reset_calls());

  // The real dispatcher would call cursor->CachedContinue(), so do that:
  scoped_ptr<WebIDBCallbacks> callbacks(new MockContinueCallbacks());
  cursor.CachedContinue(callbacks.get());

  // Now the cursor should have reset the rest of the cache.
  EXPECT_EQ(1, dispatcher_->reset_calls());
  EXPECT_EQ(1, dispatcher_->last_used_count());
}

}  // namespace content
<commit_msg>Disable WebIDBCursorImplTest.PrefetchTest & AdvancePrefetchTest<commit_after>// Copyright 2013 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/memory/scoped_ptr.h"
#include "base/values.h"
#include "content/child/indexed_db/indexed_db_dispatcher.h"
#include "content/child/indexed_db/indexed_db_key_builders.h"
#include "content/child/indexed_db/webidbcursor_impl.h"
#include "content/child/thread_safe_sender.h"
#include "content/common/indexed_db/indexed_db_key.h"
#include "ipc/ipc_sync_message_filter.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebData.h"
#include "third_party/WebKit/public/platform/WebIDBCallbacks.h"

using blink::WebBlobInfo;
using blink::WebData;
using blink::WebIDBCallbacks;
using blink::WebIDBDatabase;
using blink::WebIDBKey;
using blink::WebIDBKeyTypeNumber;
using blink::WebVector;

namespace content {

namespace {

class MockDispatcher : public IndexedDBDispatcher {
 public:
  explicit MockDispatcher(ThreadSafeSender* thread_safe_sender)
      : IndexedDBDispatcher(thread_safe_sender),
        prefetch_calls_(0),
        last_prefetch_count_(0),
        reset_calls_(0),
        last_used_count_(0),
        advance_calls_(0),
        continue_calls_(0),
        destroyed_cursor_id_(0) {}

  virtual void RequestIDBCursorPrefetch(int n,
                                        WebIDBCallbacks* callbacks,
                                        int32 ipc_cursor_id) OVERRIDE {
    ++prefetch_calls_;
    last_prefetch_count_ = n;
    callbacks_.reset(callbacks);
  }

  virtual void RequestIDBCursorPrefetchReset(int used_prefetches,
                                             int unused_prefetches,
                                             int32 ipc_cursor_id) OVERRIDE {
    ++reset_calls_;
    last_used_count_ = used_prefetches;
  }

  virtual void RequestIDBCursorAdvance(unsigned long count,
                                       WebIDBCallbacks* callbacks,
                                       int32 ipc_cursor_id,
                                       int64 transaction_id) OVERRIDE {
    ++advance_calls_;
    callbacks_.reset(callbacks);
  }

  virtual void RequestIDBCursorContinue(const IndexedDBKey& key,
                                        const IndexedDBKey& primary_key,
                                        WebIDBCallbacks* callbacks,
                                        int32 ipc_cursor_id,
                                        int64 transaction_id) OVERRIDE {
    ++continue_calls_;
    callbacks_.reset(callbacks);
  }

  virtual void CursorDestroyed(int32 ipc_cursor_id) OVERRIDE {
    destroyed_cursor_id_ = ipc_cursor_id;
  }

  int prefetch_calls() { return prefetch_calls_; }
  int last_prefetch_count() { return last_prefetch_count_; }
  int reset_calls() { return reset_calls_; }
  int last_used_count() { return last_used_count_; }
  int advance_calls() { return advance_calls_; }
  int continue_calls() { return continue_calls_; }
  int32 destroyed_cursor_id() { return destroyed_cursor_id_; }

 private:
  int prefetch_calls_;
  int last_prefetch_count_;
  int reset_calls_;
  int last_used_count_;
  int advance_calls_;
  int continue_calls_;
  int32 destroyed_cursor_id_;
  scoped_ptr<WebIDBCallbacks> callbacks_;
};

class MockContinueCallbacks : public WebIDBCallbacks {
 public:
  MockContinueCallbacks(IndexedDBKey* key = 0,
                        WebVector<WebBlobInfo>* webBlobInfo = 0)
      : key_(key), webBlobInfo_(webBlobInfo) {}

  virtual void onSuccess(const WebIDBKey& key,
                         const WebIDBKey& primaryKey,
                         const WebData& value,
                         const WebVector<WebBlobInfo>& webBlobInfo) OVERRIDE {
    if (key_)
      *key_ = IndexedDBKeyBuilder::Build(key);
    if (webBlobInfo_)
      *webBlobInfo_ = webBlobInfo;
  }

 private:
  IndexedDBKey* key_;
  WebVector<WebBlobInfo>* webBlobInfo_;
};

}  // namespace

class WebIDBCursorImplTest : public testing::Test {
 public:
  WebIDBCursorImplTest() {
    null_key_.assignNull();
    sync_message_filter_ = new IPC::SyncMessageFilter(NULL);
    thread_safe_sender_ = new ThreadSafeSender(
        base::MessageLoopProxy::current(), sync_message_filter_.get());
    dispatcher_ =
        make_scoped_ptr(new MockDispatcher(thread_safe_sender_.get()));
  }

 protected:
  WebIDBKey null_key_;
  scoped_refptr<ThreadSafeSender> thread_safe_sender_;
  scoped_ptr<MockDispatcher> dispatcher_;

 private:
  scoped_refptr<IPC::SyncMessageFilter> sync_message_filter_;

  DISALLOW_COPY_AND_ASSIGN(WebIDBCursorImplTest);
};

// Fails under Linux ASAN: crbug.com/389647
#if defined(OS_LINUX)
#define MAYBE_PrefetchTest DISABLED_PrefetchTest
#else
#define MAYBE_PrefetchTest PrefetchTest
#endif
TEST_F(WebIDBCursorImplTest, MAYBE_PrefetchTest) {
  const int64 transaction_id = 1;
  {
    WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId,
                            transaction_id,
                            thread_safe_sender_.get());

    // Call continue() until prefetching should kick in.
    int continue_calls = 0;
    EXPECT_EQ(dispatcher_->continue_calls(), 0);
    for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
      cursor.continueFunction(null_key_, new MockContinueCallbacks());
      EXPECT_EQ(++continue_calls, dispatcher_->continue_calls());
      EXPECT_EQ(0, dispatcher_->prefetch_calls());
    }

    // Do enough repetitions to verify that the count grows each time,
    // but not so many that the maximum limit is hit.
    const int kPrefetchRepetitions = 5;

    int expected_key = 0;
    int last_prefetch_count = 0;
    for (int repetitions = 0; repetitions < kPrefetchRepetitions;
         ++repetitions) {
      // Initiate the prefetch
      cursor.continueFunction(null_key_, new MockContinueCallbacks());
      EXPECT_EQ(continue_calls, dispatcher_->continue_calls());
      EXPECT_EQ(repetitions + 1, dispatcher_->prefetch_calls());

      // Verify that the requested count has increased since last time.
      int prefetch_count = dispatcher_->last_prefetch_count();
      EXPECT_GT(prefetch_count, last_prefetch_count);
      last_prefetch_count = prefetch_count;

      // Fill the prefetch cache as requested.
      std::vector<IndexedDBKey> keys;
      std::vector<IndexedDBKey> primary_keys(prefetch_count);
      std::vector<WebData> values(prefetch_count);
      std::vector<WebVector<WebBlobInfo> > blob_info;
      for (int i = 0; i < prefetch_count; ++i) {
        keys.push_back(IndexedDBKey(expected_key + i, WebIDBKeyTypeNumber));
        blob_info.push_back(
            WebVector<WebBlobInfo>(static_cast<size_t>(expected_key + i)));
      }
      cursor.SetPrefetchData(keys, primary_keys, values, blob_info);

      // Note that the real dispatcher would call cursor->CachedContinue()
      // immediately after cursor->SetPrefetchData() to service the request
      // that initiated the prefetch.

      // Verify that the cache is used for subsequent continue() calls.
      for (int i = 0; i < prefetch_count; ++i) {
        IndexedDBKey key;
        WebVector<WebBlobInfo> web_blob_info;
        cursor.continueFunction(
            null_key_, new MockContinueCallbacks(&key, &web_blob_info));
        EXPECT_EQ(continue_calls, dispatcher_->continue_calls());
        EXPECT_EQ(repetitions + 1, dispatcher_->prefetch_calls());

        EXPECT_EQ(WebIDBKeyTypeNumber, key.type());
        EXPECT_EQ(expected_key, static_cast<int>(web_blob_info.size()));
        EXPECT_EQ(expected_key++, key.number());
      }
    }
  }

  EXPECT_EQ(dispatcher_->destroyed_cursor_id(),
            WebIDBCursorImpl::kInvalidCursorId);
}

// Fails under Linux ASAN: crbug.com/389647
#if defined(OS_LINUX)
#define MAYBE_AdvancePrefetchTest DISABLED_AdvancePrefetchTest
#else
#define MAYBE_AdvancePrefetchTest AdvancePrefetchTest
#endif
TEST_F(WebIDBCursorImplTest, MAYBE_AdvancePrefetchTest) {
  const int64 transaction_id = 1;
  WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId,
                          transaction_id,
                          thread_safe_sender_.get());

  // Call continue() until prefetching should kick in.
  EXPECT_EQ(0, dispatcher_->continue_calls());
  for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
    cursor.continueFunction(null_key_, new MockContinueCallbacks());
  }
  EXPECT_EQ(0, dispatcher_->prefetch_calls());

  // Initiate the prefetch
  cursor.continueFunction(null_key_, new MockContinueCallbacks());

  EXPECT_EQ(1, dispatcher_->prefetch_calls());
  EXPECT_EQ(static_cast<int>(WebIDBCursorImpl::kPrefetchContinueThreshold),
            dispatcher_->continue_calls());
  EXPECT_EQ(0, dispatcher_->advance_calls());

  const int prefetch_count = dispatcher_->last_prefetch_count();

  // Fill the prefetch cache as requested.
  int expected_key = 0;
  std::vector<IndexedDBKey> keys;
  std::vector<IndexedDBKey> primary_keys(prefetch_count);
  std::vector<WebData> values(prefetch_count);
  std::vector<WebVector<WebBlobInfo> > blob_info;
  for (int i = 0; i < prefetch_count; ++i) {
    keys.push_back(IndexedDBKey(expected_key + i, WebIDBKeyTypeNumber));
    blob_info.push_back(
        WebVector<WebBlobInfo>(static_cast<size_t>(expected_key + i)));
  }
  cursor.SetPrefetchData(keys, primary_keys, values, blob_info);

  // Note that the real dispatcher would call cursor->CachedContinue()
  // immediately after cursor->SetPrefetchData() to service the request
  // that initiated the prefetch.

  // Need at least this many in the cache for the test steps.
  ASSERT_GE(prefetch_count, 5);

  // IDBCursor.continue()
  IndexedDBKey key;
  cursor.continueFunction(null_key_, new MockContinueCallbacks(&key));
  EXPECT_EQ(0, key.number());

  // IDBCursor.advance(1)
  cursor.advance(1, new MockContinueCallbacks(&key));
  EXPECT_EQ(1, key.number());

  // IDBCursor.continue()
  cursor.continueFunction(null_key_, new MockContinueCallbacks(&key));
  EXPECT_EQ(2, key.number());

  // IDBCursor.advance(2)
  cursor.advance(2, new MockContinueCallbacks(&key));
  EXPECT_EQ(4, key.number());

  EXPECT_EQ(0, dispatcher_->advance_calls());

  // IDBCursor.advance(lots) - beyond the fetched amount
  cursor.advance(WebIDBCursorImpl::kMaxPrefetchAmount,
                 new MockContinueCallbacks(&key));
  EXPECT_EQ(1, dispatcher_->advance_calls());
  EXPECT_EQ(1, dispatcher_->prefetch_calls());
  EXPECT_EQ(static_cast<int>(WebIDBCursorImpl::kPrefetchContinueThreshold),
            dispatcher_->continue_calls());
}

TEST_F(WebIDBCursorImplTest, PrefetchReset) {
  const int64 transaction_id = 1;
  WebIDBCursorImpl cursor(WebIDBCursorImpl::kInvalidCursorId,
                          transaction_id,
                          thread_safe_sender_.get());

  // Call continue() until prefetching should kick in.
  int continue_calls = 0;
  EXPECT_EQ(dispatcher_->continue_calls(), 0);
  for (int i = 0; i < WebIDBCursorImpl::kPrefetchContinueThreshold; ++i) {
    cursor.continueFunction(null_key_, new MockContinueCallbacks());
    EXPECT_EQ(++continue_calls, dispatcher_->continue_calls());
    EXPECT_EQ(0, dispatcher_->prefetch_calls());
  }

  // Initiate the prefetch
  cursor.continueFunction(null_key_, new MockContinueCallbacks());
  EXPECT_EQ(continue_calls, dispatcher_->continue_calls());
  EXPECT_EQ(1, dispatcher_->prefetch_calls());
  EXPECT_EQ(0, dispatcher_->reset_calls());

  // Now invalidate it
  cursor.ResetPrefetchCache();

  // No reset should have been sent since nothing has been received yet.
  EXPECT_EQ(0, dispatcher_->reset_calls());

  // Fill the prefetch cache as requested.
  int prefetch_count = dispatcher_->last_prefetch_count();
  std::vector<IndexedDBKey> keys(prefetch_count);
  std::vector<IndexedDBKey> primary_keys(prefetch_count);
  std::vector<WebData> values(prefetch_count);
  std::vector<WebVector<WebBlobInfo> > blob_info(prefetch_count);
  cursor.SetPrefetchData(keys, primary_keys, values, blob_info);

  // No reset should have been sent since prefetch data hasn't been used.
  EXPECT_EQ(0, dispatcher_->reset_calls());

  // The real dispatcher would call cursor->CachedContinue(), so do that:
  scoped_ptr<WebIDBCallbacks> callbacks(new MockContinueCallbacks());
  cursor.CachedContinue(callbacks.get());

  // Now the cursor should have reset the rest of the cache.
  EXPECT_EQ(1, dispatcher_->reset_calls());
  EXPECT_EQ(1, dispatcher_->last_used_count());
}

}  // namespace content
<|endoftext|>