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

#include "webkit/fileapi/file_writer_delegate.h"

#include "base/message_loop.h"
#include "net/base/net_errors.h"
#include "webkit/fileapi/file_system_operation.h"

namespace fileapi {

static const int kReadBufSize = 32768;

FileWriterDelegate::FileWriterDelegate(
    FileSystemOperation* file_system_operation,
    int64 offset)
    : file_system_operation_(file_system_operation),
      file_(base::kInvalidPlatformFileValue),
      offset_(offset),
      bytes_read_backlog_(0),
      bytes_written_(0),
      bytes_read_(0),
      io_buffer_(new net::IOBufferWithSize(kReadBufSize)),
      callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
      method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
}

FileWriterDelegate::~FileWriterDelegate() {
}

void FileWriterDelegate::Start(base::PlatformFile file, URLRequest* request) {
  file_ = file;
  request_ = request;
  file_stream_.reset(
      new net::FileStream(
        file,
        base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE |
            base::PLATFORM_FILE_ASYNC));
  request_->Start();
}

void FileWriterDelegate::OnReceivedRedirect(
    URLRequest* request, const GURL& new_url, bool* defer_redirect) {
  NOTREACHED();
  OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}

void FileWriterDelegate::OnAuthRequired(
    URLRequest* request, net::AuthChallengeInfo* auth_info) {
  NOTREACHED();
  OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}

void FileWriterDelegate::OnCertificateRequested(
    URLRequest* request, net::SSLCertRequestInfo* cert_request_info) {
  NOTREACHED();
  OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}

void FileWriterDelegate::OnSSLCertificateError(
    URLRequest* request, int cert_error, net::X509Certificate* cert) {
  NOTREACHED();
  OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}

void FileWriterDelegate::OnResponseStarted(URLRequest* request) {
  DCHECK_EQ(request_, request);
  if (!request->status().is_success()) {
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
    return;
  }
  int64 error = file_stream_->Seek(net::FROM_BEGIN, offset_);
  if (error != offset_) {
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
    return;
  }
  Read();
}

void FileWriterDelegate::OnReadCompleted(URLRequest* request, int bytes_read) {
  DCHECK_EQ(request_, request);
  if (!request->status().is_success()) {
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
    return;
  }
  OnDataReceived(bytes_read);
}

void FileWriterDelegate::Read() {
  bytes_written_ = 0;
  bytes_read_ = 0;
  if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) {
    MessageLoop::current()->PostTask(
        FROM_HERE,
        method_factory_.NewRunnableMethod(
            &FileWriterDelegate::OnDataReceived, bytes_read_));
  } else if (!request_->status().is_io_pending()) {
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
  }
}

void FileWriterDelegate::OnDataReceived(int bytes_read) {
  bytes_read_ = bytes_read;
  if (!bytes_read_) {  // We're done.
    OnProgress(0, true);
  } else {
    // This could easily be optimized to rotate between a pool of buffers, so
    // that we could read and write at the same time.  It's not yet clear that
    // it's necessary.
    Write();
  }
}

void FileWriterDelegate::Write() {
  int write_response = file_stream_->Write(
        io_buffer_->data() + bytes_written_,
        bytes_read_ - bytes_written_,
        callback_factory_.NewCallback(&FileWriterDelegate::OnDataWritten));
  if (write_response > 0)
    MessageLoop::current()->PostTask(
        FROM_HERE,
        method_factory_.NewRunnableMethod(
            &FileWriterDelegate::OnDataWritten, write_response));
  else if (net::ERR_IO_PENDING != write_response)
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
}

void FileWriterDelegate::OnDataWritten(int write_response) {
  if (write_response > 0) {
    OnProgress(write_response, false);
    bytes_written_ += write_response;
    if (bytes_written_ == bytes_read_)
      Read();
    else
      Write();
  } else {
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
  }
}

void FileWriterDelegate::OnError(base::PlatformFileError error) {
  request_->Cancel();
  file_system_operation_->DidWrite(error, 0, true);
}

void FileWriterDelegate::OnProgress(int bytes_read, bool done) {
  DCHECK(bytes_read + bytes_read_backlog_ >= bytes_read_backlog_);
  static const int kMinProgressDelayMS = 200;
  base::Time currentTime = base::Time::Now();
  if (done || last_progress_event_time_.is_null() ||
      (currentTime - last_progress_event_time_).InMilliseconds() >
          kMinProgressDelayMS) {
    file_system_operation_->DidWrite(base::PLATFORM_FILE_OK,
                                     bytes_read + bytes_read_backlog_, done);
    last_progress_event_time_ = currentTime;
    bytes_read_backlog_ = 0;
  } else {
    bytes_read_backlog_ += bytes_read;
  }
}

}  // namespace fileapi

<commit_msg>FileWriter: Defend against being deleted in the FileWriterDelegate completion callback.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/fileapi/file_writer_delegate.h"

#include "base/message_loop.h"
#include "net/base/net_errors.h"
#include "webkit/fileapi/file_system_operation.h"

namespace fileapi {

static const int kReadBufSize = 32768;

FileWriterDelegate::FileWriterDelegate(
    FileSystemOperation* file_system_operation,
    int64 offset)
    : file_system_operation_(file_system_operation),
      file_(base::kInvalidPlatformFileValue),
      offset_(offset),
      bytes_read_backlog_(0),
      bytes_written_(0),
      bytes_read_(0),
      io_buffer_(new net::IOBufferWithSize(kReadBufSize)),
      callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
      method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
}

FileWriterDelegate::~FileWriterDelegate() {
}

void FileWriterDelegate::Start(base::PlatformFile file, URLRequest* request) {
  file_ = file;
  request_ = request;
  file_stream_.reset(
      new net::FileStream(
        file,
        base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE |
            base::PLATFORM_FILE_ASYNC));
  request_->Start();
}

void FileWriterDelegate::OnReceivedRedirect(
    URLRequest* request, const GURL& new_url, bool* defer_redirect) {
  NOTREACHED();
  OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}

void FileWriterDelegate::OnAuthRequired(
    URLRequest* request, net::AuthChallengeInfo* auth_info) {
  NOTREACHED();
  OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}

void FileWriterDelegate::OnCertificateRequested(
    URLRequest* request, net::SSLCertRequestInfo* cert_request_info) {
  NOTREACHED();
  OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}

void FileWriterDelegate::OnSSLCertificateError(
    URLRequest* request, int cert_error, net::X509Certificate* cert) {
  NOTREACHED();
  OnError(base::PLATFORM_FILE_ERROR_SECURITY);
}

void FileWriterDelegate::OnResponseStarted(URLRequest* request) {
  DCHECK_EQ(request_, request);
  if (!request->status().is_success()) {
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
    return;
  }
  int64 error = file_stream_->Seek(net::FROM_BEGIN, offset_);
  if (error != offset_) {
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
    return;
  }
  Read();
}

void FileWriterDelegate::OnReadCompleted(URLRequest* request, int bytes_read) {
  DCHECK_EQ(request_, request);
  if (!request->status().is_success()) {
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
    return;
  }
  OnDataReceived(bytes_read);
}

void FileWriterDelegate::Read() {
  bytes_written_ = 0;
  bytes_read_ = 0;
  if (request_->Read(io_buffer_.get(), io_buffer_->size(), &bytes_read_)) {
    MessageLoop::current()->PostTask(
        FROM_HERE,
        method_factory_.NewRunnableMethod(
            &FileWriterDelegate::OnDataReceived, bytes_read_));
  } else if (!request_->status().is_io_pending()) {
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
  }
}

void FileWriterDelegate::OnDataReceived(int bytes_read) {
  bytes_read_ = bytes_read;
  if (!bytes_read_) {  // We're done.
    OnProgress(0, true);
  } else {
    // This could easily be optimized to rotate between a pool of buffers, so
    // that we could read and write at the same time.  It's not yet clear that
    // it's necessary.
    Write();
  }
}

void FileWriterDelegate::Write() {
  int write_response = file_stream_->Write(
        io_buffer_->data() + bytes_written_,
        bytes_read_ - bytes_written_,
        callback_factory_.NewCallback(&FileWriterDelegate::OnDataWritten));
  if (write_response > 0)
    MessageLoop::current()->PostTask(
        FROM_HERE,
        method_factory_.NewRunnableMethod(
            &FileWriterDelegate::OnDataWritten, write_response));
  else if (net::ERR_IO_PENDING != write_response)
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
}

void FileWriterDelegate::OnDataWritten(int write_response) {
  if (write_response > 0) {
    OnProgress(write_response, false);
    bytes_written_ += write_response;
    if (bytes_written_ == bytes_read_)
      Read();
    else
      Write();
  } else {
    OnError(base::PLATFORM_FILE_ERROR_FAILED);
  }
}

void FileWriterDelegate::OnError(base::PlatformFileError error) {
  request_->Cancel();
  file_system_operation_->DidWrite(error, 0, true);
}

void FileWriterDelegate::OnProgress(int bytes_read, bool done) {
  DCHECK(bytes_read + bytes_read_backlog_ >= bytes_read_backlog_);
  static const int kMinProgressDelayMS = 200;
  base::Time currentTime = base::Time::Now();
  if (done || last_progress_event_time_.is_null() ||
      (currentTime - last_progress_event_time_).InMilliseconds() >
          kMinProgressDelayMS) {
    bytes_read += bytes_read_backlog_;
    last_progress_event_time_ = currentTime;
    bytes_read_backlog_ = 0;
    file_system_operation_->DidWrite(
        base::PLATFORM_FILE_OK, bytes_read, done);
    return;
  }
  bytes_read_backlog_ += bytes_read;
}

}  // namespace fileapi

<|endoftext|>


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

#include "config.h"

#include "AtomicString.h"
#include "Document.h"
#include "Event.h"
#include "EventListener.h"
#include "EventNames.h"
#include "EventTarget.h"
#include "HTMLFrameOwnerElement.h"
#include "markup.h"
#include "MutationEvent.h"
#include "Node.h"
#include "PlatformString.h"
#include "Text.h"
#include <wtf/OwnPtr.h>
#include <wtf/Vector.h>
#undef LOG

#include "base/values.h"
#include "webkit/glue/devtools/dom_agent_impl.h"
#include "webkit/glue/glue_util.h"

using namespace WebCore;

// static
PassRefPtr<DomAgentImpl::EventListenerWrapper>
    DomAgentImpl::EventListenerWrapper::Create(
        DomAgentImpl* dom_agent_impl) {
  return adoptRef(new EventListenerWrapper(dom_agent_impl));
}

DomAgentImpl::EventListenerWrapper::EventListenerWrapper(
    DomAgentImpl* dom_agent_impl) : dom_agent_impl_(dom_agent_impl) {
}

void DomAgentImpl::EventListenerWrapper::handleEvent(
    Event* event,
    bool isWindowEvent) {
  dom_agent_impl_->handleEvent(event, isWindowEvent);
}

DomAgentImpl::DomAgentImpl(DomAgentDelegate* delegate)
    : delegate_(delegate),
      last_node_id_(1),
      document_element_requested_(false) {
  event_listener_ = EventListenerWrapper::Create(this);
}

DomAgentImpl::~DomAgentImpl() {
  SetDocument(NULL);
}

void DomAgentImpl::SetDocument(Document* doc) {
  DiscardBindings();

  ListHashSet<RefPtr<Document> > copy = documents_;
  for (ListHashSet<RefPtr<Document> >::iterator it = copy.begin();
       it != copy.end(); ++it) {
    StopListening((*it).get());
  }
  ASSERT(documents_.size() == 0);

  if (doc) {
    StartListening(doc);
    if (document_element_requested_) {
      GetDocumentElement();
      document_element_requested_ = false;
    }
  }
}

void DomAgentImpl::StartListening(Document* doc) {
  if (documents_.find(doc) != documents_.end())
    return;
  doc->addEventListener(eventNames().DOMContentLoadedEvent, event_listener_,
      false);
  doc->addEventListener(eventNames().DOMNodeInsertedEvent, event_listener_,
      false);
  doc->addEventListener(eventNames().DOMNodeRemovedEvent, event_listener_,
      false);
  doc->addEventListener(eventNames().DOMNodeRemovedFromDocumentEvent,
      event_listener_, false);
  doc->addEventListener(eventNames().DOMAttrModifiedEvent, event_listener_,
      false);
  documents_.add(doc);
}

void DomAgentImpl::StopListening(Document* doc) {
  doc->removeEventListener(eventNames().DOMContentLoadedEvent,
      event_listener_.get(), false);
  doc->removeEventListener(eventNames().DOMNodeInsertedEvent,
      event_listener_.get(), false);
  doc->removeEventListener(eventNames().DOMNodeRemovedEvent,
      event_listener_.get(), false);
  doc->removeEventListener(eventNames().DOMNodeRemovedFromDocumentEvent,
      event_listener_.get(), false);
  doc->removeEventListener(eventNames().DOMAttrModifiedEvent,
      event_listener_.get(), false);
  documents_.remove(doc);
}

int DomAgentImpl::Bind(Node* node) {
  HashMap<Node*, int>::iterator it = node_to_id_.find(node);
  if (it != node_to_id_.end())
    return it->second;
  int id = last_node_id_++;
  node_to_id_.set(node, id);
  id_to_node_.set(id, node);
  return id;
}

void DomAgentImpl::Unbind(Node* node) {
  if (node->isFrameOwnerElement()) {
    const HTMLFrameOwnerElement* frame_owner =
        static_cast<const HTMLFrameOwnerElement*>(node);
    StopListening(frame_owner->contentDocument());
  }

  HashMap<Node*, int>::iterator it = node_to_id_.find(node);
  if (it != node_to_id_.end()) {
    id_to_node_.remove(id_to_node_.find(it->second));
    children_requested_.remove(children_requested_.find(it->second));
    node_to_id_.remove(it);
  }
}

void DomAgentImpl::DiscardBindings() {
  node_to_id_.clear();
  id_to_node_.clear();
  children_requested_.clear();
}

Node* DomAgentImpl::GetNodeForId(int id) {
  HashMap<int, Node*>::iterator it = id_to_node_.find(id);
  if (it != id_to_node_.end()) {
    return it->second;
  }
  return NULL;
}

int DomAgentImpl::GetIdForNode(Node* node) {
  if (node == NULL) {
    return 0;
  }
  HashMap<Node*, int>::iterator it = node_to_id_.find(node);
  if (it != node_to_id_.end()) {
    return it->second;
  }
  return 0;
}

void DomAgentImpl::handleEvent(Event* event, bool isWindowEvent) {
  AtomicString type = event->type();
  Node* node = event->target()->toNode();

  // Remove mapping entry if necessary.
  if (type == eventNames().DOMNodeRemovedFromDocumentEvent) {
    Unbind(node);
    return;
  }

  if (type == eventNames().DOMAttrModifiedEvent) {
    int id = GetIdForNode(node);
    if (!id) {
      // Node is not mapped yet -> ignore the event.
      return;
    }
    Element* element = static_cast<Element*>(node);
    OwnPtr<Value> attributesValue(BuildValueForElementAttributes(element));
    delegate_->AttributesUpdated(id, *attributesValue.get());
  } else if (type == eventNames().DOMNodeInsertedEvent) {
    Node* parent = static_cast<MutationEvent*>(event)->relatedNode();
    int parent_id = GetIdForNode(parent);
    if (!parent_id) {
      // Parent is not mapped yet -> ignore the event.
      return;
    }
    HashSet<int>::iterator cit = children_requested_.find(parent_id);
    if (cit == children_requested_.end()) {
      // No children are mapped yet -> only notify on changes of hasChildren.
      delegate_->HasChildrenUpdated(parent_id, true);
    } else {
      // Children have been requested -> return value of a new child.
      int prev_id = GetIdForNode(node->previousSibling());
      OwnPtr<Value> value(BuildValueForNode(node, 0));
      delegate_->ChildNodeInserted(parent_id, prev_id, *value.get());
    }
  } else if (type == eventNames().DOMNodeRemovedEvent) {
    Node* parent = static_cast<MutationEvent*>(event)->relatedNode();
    int parent_id = GetIdForNode(parent);
    if (!parent_id) {
      // Parent is not mapped yet -> ignore the event.
      return;
    }
    HashSet<int>::iterator cit = children_requested_.find(parent_id);
    if (cit == children_requested_.end()) {
      // No children are mapped yet -> only notify on changes of hasChildren.
      if (parent->childNodeCount() == 1)
        delegate_->HasChildrenUpdated(parent_id, false);
    } else {
      int id = GetIdForNode(node);
      delegate_->ChildNodeRemoved(parent_id, id);
    }
  } else if (type == eventNames().DOMContentLoadedEvent) {
    //TODO(pfeldman): handle content load event.
  }
}

void DomAgentImpl::GetDocumentElement() {
  if (documents_.size() > 0) {
    OwnPtr<Value> value(
        BuildValueForNode((*documents_.begin())->documentElement(), 0));
    delegate_->DocumentElementUpdated(*value.get());
  } else {
    document_element_requested_ = true;
  }
}

void DomAgentImpl::GetChildNodes(int element_id) {
  Node* node = GetNodeForId(element_id);
  if (!node || (node->nodeType() != Node::ELEMENT_NODE))
    return;

  Element* element = static_cast<Element*>(node);
  OwnPtr<Value> children(BuildValueForElementChildren(element, 1));
  children_requested_.add(element_id);
  delegate_->ChildNodesUpdated(element_id, *children.get());
}

int DomAgentImpl::GetPathToNode(Node* node_to_select) {
  ASSERT(node_to_select);  // Invalid input

  // Return id in case the node is known.
  int result = GetIdForNode(node_to_select);
  if (result)
    return result;

  Element* element = InnerParentElement(node_to_select);
  ASSERT(element);  // Node is detached or is a document itself

  Vector<Element*> path;
  while (element && !GetIdForNode(element)) {
    path.append(element);
    element = InnerParentElement(element);
  }
  // element is known to the client
  ASSERT(element);
  path.append(element);

  for (int i = path.size() - 1; i >= 0; --i) {
    element = path.at(i);
    OwnPtr<Value> children(BuildValueForElementChildren(element, 1));
    delegate_->ChildNodesUpdated(GetIdForNode(element), *children.get());
  }
  return GetIdForNode(node_to_select);
}

void DomAgentImpl::SetAttribute(
    int element_id,
    const String& name,
    const String& value) {
  Node* node = GetNodeForId(element_id);
  if (node && (node->nodeType() == Node::ELEMENT_NODE)) {
    Element* element = static_cast<Element*>(node);
        ExceptionCode ec = 0;
    element->setAttribute(name, value, ec);
  }
}

void DomAgentImpl::RemoveAttribute(int element_id, const String& name) {
  Node* node = GetNodeForId(element_id);
  if (node && (node->nodeType() == Node::ELEMENT_NODE)) {
    Element* element = static_cast<Element*>(node);
    ExceptionCode ec = 0;
    element->removeAttribute(name, ec);
  }
}

void DomAgentImpl::SetTextNodeValue(int element_id, const String& value) {
  Node* node = GetNodeForId(element_id);
  if (node && (node->nodeType() == Node::TEXT_NODE)) {
    Text* text_node = static_cast<Text*>(node);
    ExceptionCode ec = 0;
    // TODO(pfeldman): Add error handling
    text_node->replaceWholeText(value, ec);
  }
}

ListValue* DomAgentImpl::BuildValueForNode(Node* node, int depth) {
  OwnPtr<ListValue> value(new ListValue());
  int id = Bind(node);
  String nodeName;
  String nodeValue;

  switch (node->nodeType()) {
    case Node::TEXT_NODE:
    case Node::COMMENT_NODE:
      nodeValue = node->nodeValue();
      break;
    case Node::ATTRIBUTE_NODE:
    case Node::DOCUMENT_NODE:
    case Node::DOCUMENT_FRAGMENT_NODE:
      break;
    case Node::ELEMENT_NODE:
    default: {
      nodeName = node->nodeName();
      break;
    }
  }

  value->Append(Value::CreateIntegerValue(id));
  value->Append(Value::CreateIntegerValue(node->nodeType()));
  value->Append(Value::CreateStringValue(
      webkit_glue::StringToStdWString(nodeName)));
  value->Append(Value::CreateStringValue(
      webkit_glue::StringToStdWString(nodeValue)));

  if (node->nodeType() == Node::ELEMENT_NODE) {
    Element* element = static_cast<Element*>(node);
    value->Append(BuildValueForElementAttributes(element));
    int nodeCount = InnerChildNodeCount(element);
    value->Append(Value::CreateIntegerValue(nodeCount));
    OwnPtr<ListValue> children(BuildValueForElementChildren(element, depth));
    if (children->GetSize() > 0) {
      value->Append(children.release());
    }
  }
  return value.release();
}

ListValue* DomAgentImpl::BuildValueForElementAttributes(Element* element) {
  OwnPtr<ListValue> attributesValue(new ListValue());
  // Go through all attributes and serialize them.
  const NamedAttrMap *attrMap = element->attributes(true);
  if (!attrMap) {
    return attributesValue.release();
  }
  unsigned numAttrs = attrMap->length();
  for (unsigned i = 0; i < numAttrs; i++) {
    // Add attribute pair
    const Attribute *attribute = attrMap->attributeItem(i);
    OwnPtr<Value> name(Value::CreateStringValue(
        webkit_glue::StringToStdWString(attribute->name().toString())));
    OwnPtr<Value> value(Value::CreateStringValue(
        webkit_glue::StringToStdWString(attribute->value())));
    attributesValue->Append(name.release());
    attributesValue->Append(value.release());
  }
  return attributesValue.release();
}

ListValue* DomAgentImpl::BuildValueForElementChildren(
    Element* element,
    int depth) {
  OwnPtr<ListValue> children(new ListValue());
  if (depth == 0) {
    // Special case the_only text child.
    if (element->childNodeCount() == 1) {
      Node *child = element->firstChild();
      if (child->nodeType() == Node::TEXT_NODE) {
        children->Append(BuildValueForNode(child, 0));
      }
    }
    return children.release();
  } else if (depth > 0) {
    depth--;
  }

  for (Node *child = InnerFirstChild(element); child != NULL;
       child = child->nextSibling()) {
    children->Append(BuildValueForNode(child, depth));
  }
  return children.release();
}

Node* DomAgentImpl::InnerFirstChild(Node* node) {
  if (node->isFrameOwnerElement()) {
    HTMLFrameOwnerElement* frame_owner =
        static_cast<HTMLFrameOwnerElement*>(node);
    Document* doc = frame_owner->contentDocument();
    StartListening(doc);
    return doc->firstChild();
  } else {
    return node->firstChild();
  }
}

int DomAgentImpl::InnerChildNodeCount(Node* node) {
  if (node->isFrameOwnerElement()) {
    return 1;
  } else {
    return node->childNodeCount();
  }
}

Element* DomAgentImpl::InnerParentElement(Node* node) {
  Element* element = node->parentElement();
  if (!element) {
    return node->ownerDocument()->ownerElement();
  }
  return element;
}
<commit_msg>Switch to useCapture in order to handle DOMNodeRemovedFromDocument events.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"

#include "AtomicString.h"
#include "Document.h"
#include "Event.h"
#include "EventListener.h"
#include "EventNames.h"
#include "EventTarget.h"
#include "HTMLFrameOwnerElement.h"
#include "markup.h"
#include "MutationEvent.h"
#include "Node.h"
#include "PlatformString.h"
#include "Text.h"
#include <wtf/OwnPtr.h>
#include <wtf/Vector.h>
#undef LOG

#include "base/values.h"
#include "webkit/glue/devtools/dom_agent_impl.h"
#include "webkit/glue/glue_util.h"

using namespace WebCore;

// static
PassRefPtr<DomAgentImpl::EventListenerWrapper>
    DomAgentImpl::EventListenerWrapper::Create(
        DomAgentImpl* dom_agent_impl) {
  return adoptRef(new EventListenerWrapper(dom_agent_impl));
}

DomAgentImpl::EventListenerWrapper::EventListenerWrapper(
    DomAgentImpl* dom_agent_impl) : dom_agent_impl_(dom_agent_impl) {
}

void DomAgentImpl::EventListenerWrapper::handleEvent(
    Event* event,
    bool isWindowEvent) {
  dom_agent_impl_->handleEvent(event, isWindowEvent);
}

DomAgentImpl::DomAgentImpl(DomAgentDelegate* delegate)
    : delegate_(delegate),
      last_node_id_(1),
      document_element_requested_(false) {
  event_listener_ = EventListenerWrapper::Create(this);
}

DomAgentImpl::~DomAgentImpl() {
  SetDocument(NULL);
}

void DomAgentImpl::SetDocument(Document* doc) {
  DiscardBindings();

  ListHashSet<RefPtr<Document> > copy = documents_;
  for (ListHashSet<RefPtr<Document> >::iterator it = copy.begin();
       it != copy.end(); ++it) {
    StopListening((*it).get());
  }
  ASSERT(documents_.size() == 0);

  if (doc) {
    StartListening(doc);
    if (document_element_requested_) {
      GetDocumentElement();
      document_element_requested_ = false;
    }
  }
}

void DomAgentImpl::StartListening(Document* doc) {
  if (documents_.find(doc) != documents_.end())
    return;
  doc->addEventListener(eventNames().DOMContentLoadedEvent, event_listener_,
      false);
  doc->addEventListener(eventNames().DOMNodeInsertedEvent, event_listener_,
      false);
  doc->addEventListener(eventNames().DOMNodeRemovedEvent, event_listener_,
      false);
  doc->addEventListener(eventNames().DOMNodeRemovedFromDocumentEvent,
      event_listener_, true);
  doc->addEventListener(eventNames().DOMAttrModifiedEvent, event_listener_,
      false);
  documents_.add(doc);
}

void DomAgentImpl::StopListening(Document* doc) {
  doc->removeEventListener(eventNames().DOMContentLoadedEvent,
      event_listener_.get(), false);
  doc->removeEventListener(eventNames().DOMNodeInsertedEvent,
      event_listener_.get(), false);
  doc->removeEventListener(eventNames().DOMNodeRemovedEvent,
      event_listener_.get(), false);
  doc->removeEventListener(eventNames().DOMNodeRemovedFromDocumentEvent,
      event_listener_.get(), true);
  doc->removeEventListener(eventNames().DOMAttrModifiedEvent,
      event_listener_.get(), false);
  documents_.remove(doc);
}

int DomAgentImpl::Bind(Node* node) {
  HashMap<Node*, int>::iterator it = node_to_id_.find(node);
  if (it != node_to_id_.end())
    return it->second;
  int id = last_node_id_++;
  node_to_id_.set(node, id);
  id_to_node_.set(id, node);
  return id;
}

void DomAgentImpl::Unbind(Node* node) {
  if (node->isFrameOwnerElement()) {
    const HTMLFrameOwnerElement* frame_owner =
        static_cast<const HTMLFrameOwnerElement*>(node);
    StopListening(frame_owner->contentDocument());
  }

  HashMap<Node*, int>::iterator it = node_to_id_.find(node);
  if (it != node_to_id_.end()) {
    id_to_node_.remove(id_to_node_.find(it->second));
    children_requested_.remove(children_requested_.find(it->second));
    node_to_id_.remove(it);
  }
}

void DomAgentImpl::DiscardBindings() {
  node_to_id_.clear();
  id_to_node_.clear();
  children_requested_.clear();
}

Node* DomAgentImpl::GetNodeForId(int id) {
  HashMap<int, Node*>::iterator it = id_to_node_.find(id);
  if (it != id_to_node_.end()) {
    return it->second;
  }
  return NULL;
}

int DomAgentImpl::GetIdForNode(Node* node) {
  if (node == NULL) {
    return 0;
  }
  HashMap<Node*, int>::iterator it = node_to_id_.find(node);
  if (it != node_to_id_.end()) {
    return it->second;
  }
  return 0;
}

void DomAgentImpl::handleEvent(Event* event, bool isWindowEvent) {
  AtomicString type = event->type();
  Node* node = event->target()->toNode();

  // Remove mapping entry if necessary.
  if (type == eventNames().DOMNodeRemovedFromDocumentEvent) {
    Unbind(node);
    return;
  }

  if (type == eventNames().DOMAttrModifiedEvent) {
    int id = GetIdForNode(node);
    if (!id) {
      // Node is not mapped yet -> ignore the event.
      return;
    }
    Element* element = static_cast<Element*>(node);
    OwnPtr<Value> attributesValue(BuildValueForElementAttributes(element));
    delegate_->AttributesUpdated(id, *attributesValue.get());
  } else if (type == eventNames().DOMNodeInsertedEvent) {
    Node* parent = static_cast<MutationEvent*>(event)->relatedNode();
    int parent_id = GetIdForNode(parent);
    if (!parent_id) {
      // Parent is not mapped yet -> ignore the event.
      return;
    }
    HashSet<int>::iterator cit = children_requested_.find(parent_id);
    if (cit == children_requested_.end()) {
      // No children are mapped yet -> only notify on changes of hasChildren.
      delegate_->HasChildrenUpdated(parent_id, true);
    } else {
      // Children have been requested -> return value of a new child.
      int prev_id = GetIdForNode(node->previousSibling());
      OwnPtr<Value> value(BuildValueForNode(node, 0));
      delegate_->ChildNodeInserted(parent_id, prev_id, *value.get());
    }
  } else if (type == eventNames().DOMNodeRemovedEvent) {
    Node* parent = static_cast<MutationEvent*>(event)->relatedNode();
    int parent_id = GetIdForNode(parent);
    if (!parent_id) {
      // Parent is not mapped yet -> ignore the event.
      return;
    }
    HashSet<int>::iterator cit = children_requested_.find(parent_id);
    if (cit == children_requested_.end()) {
      // No children are mapped yet -> only notify on changes of hasChildren.
      if (parent->childNodeCount() == 1)
        delegate_->HasChildrenUpdated(parent_id, false);
    } else {
      int id = GetIdForNode(node);
      delegate_->ChildNodeRemoved(parent_id, id);
    }
  } else if (type == eventNames().DOMContentLoadedEvent) {
    //TODO(pfeldman): handle content load event.
  }
}

void DomAgentImpl::GetDocumentElement() {
  if (documents_.size() > 0) {
    OwnPtr<Value> value(
        BuildValueForNode((*documents_.begin())->documentElement(), 0));
    delegate_->DocumentElementUpdated(*value.get());
  } else {
    document_element_requested_ = true;
  }
}

void DomAgentImpl::GetChildNodes(int element_id) {
  Node* node = GetNodeForId(element_id);
  if (!node || (node->nodeType() != Node::ELEMENT_NODE))
    return;

  Element* element = static_cast<Element*>(node);
  OwnPtr<Value> children(BuildValueForElementChildren(element, 1));
  children_requested_.add(element_id);
  delegate_->ChildNodesUpdated(element_id, *children.get());
}

int DomAgentImpl::GetPathToNode(Node* node_to_select) {
  ASSERT(node_to_select);  // Invalid input

  // Return id in case the node is known.
  int result = GetIdForNode(node_to_select);
  if (result)
    return result;

  Element* element = InnerParentElement(node_to_select);
  ASSERT(element);  // Node is detached or is a document itself

  Vector<Element*> path;
  while (element && !GetIdForNode(element)) {
    path.append(element);
    element = InnerParentElement(element);
  }
  // element is known to the client
  ASSERT(element);
  path.append(element);

  for (int i = path.size() - 1; i >= 0; --i) {
    element = path.at(i);
    OwnPtr<Value> children(BuildValueForElementChildren(element, 1));
    delegate_->ChildNodesUpdated(GetIdForNode(element), *children.get());
  }
  return GetIdForNode(node_to_select);
}

void DomAgentImpl::SetAttribute(
    int element_id,
    const String& name,
    const String& value) {
  Node* node = GetNodeForId(element_id);
  if (node && (node->nodeType() == Node::ELEMENT_NODE)) {
    Element* element = static_cast<Element*>(node);
        ExceptionCode ec = 0;
    element->setAttribute(name, value, ec);
  }
}

void DomAgentImpl::RemoveAttribute(int element_id, const String& name) {
  Node* node = GetNodeForId(element_id);
  if (node && (node->nodeType() == Node::ELEMENT_NODE)) {
    Element* element = static_cast<Element*>(node);
    ExceptionCode ec = 0;
    element->removeAttribute(name, ec);
  }
}

void DomAgentImpl::SetTextNodeValue(int element_id, const String& value) {
  Node* node = GetNodeForId(element_id);
  if (node && (node->nodeType() == Node::TEXT_NODE)) {
    Text* text_node = static_cast<Text*>(node);
    ExceptionCode ec = 0;
    // TODO(pfeldman): Add error handling
    text_node->replaceWholeText(value, ec);
  }
}

ListValue* DomAgentImpl::BuildValueForNode(Node* node, int depth) {
  OwnPtr<ListValue> value(new ListValue());
  int id = Bind(node);
  String nodeName;
  String nodeValue;

  switch (node->nodeType()) {
    case Node::TEXT_NODE:
    case Node::COMMENT_NODE:
      nodeValue = node->nodeValue();
      break;
    case Node::ATTRIBUTE_NODE:
    case Node::DOCUMENT_NODE:
    case Node::DOCUMENT_FRAGMENT_NODE:
      break;
    case Node::ELEMENT_NODE:
    default: {
      nodeName = node->nodeName();
      break;
    }
  }

  value->Append(Value::CreateIntegerValue(id));
  value->Append(Value::CreateIntegerValue(node->nodeType()));
  value->Append(Value::CreateStringValue(
      webkit_glue::StringToStdWString(nodeName)));
  value->Append(Value::CreateStringValue(
      webkit_glue::StringToStdWString(nodeValue)));

  if (node->nodeType() == Node::ELEMENT_NODE) {
    Element* element = static_cast<Element*>(node);
    value->Append(BuildValueForElementAttributes(element));
    int nodeCount = InnerChildNodeCount(element);
    value->Append(Value::CreateIntegerValue(nodeCount));
    OwnPtr<ListValue> children(BuildValueForElementChildren(element, depth));
    if (children->GetSize() > 0) {
      value->Append(children.release());
    }
  }
  return value.release();
}

ListValue* DomAgentImpl::BuildValueForElementAttributes(Element* element) {
  OwnPtr<ListValue> attributesValue(new ListValue());
  // Go through all attributes and serialize them.
  const NamedAttrMap *attrMap = element->attributes(true);
  if (!attrMap) {
    return attributesValue.release();
  }
  unsigned numAttrs = attrMap->length();
  for (unsigned i = 0; i < numAttrs; i++) {
    // Add attribute pair
    const Attribute *attribute = attrMap->attributeItem(i);
    OwnPtr<Value> name(Value::CreateStringValue(
        webkit_glue::StringToStdWString(attribute->name().toString())));
    OwnPtr<Value> value(Value::CreateStringValue(
        webkit_glue::StringToStdWString(attribute->value())));
    attributesValue->Append(name.release());
    attributesValue->Append(value.release());
  }
  return attributesValue.release();
}

ListValue* DomAgentImpl::BuildValueForElementChildren(
    Element* element,
    int depth) {
  OwnPtr<ListValue> children(new ListValue());
  if (depth == 0) {
    // Special case the_only text child.
    if (element->childNodeCount() == 1) {
      Node *child = element->firstChild();
      if (child->nodeType() == Node::TEXT_NODE) {
        children->Append(BuildValueForNode(child, 0));
      }
    }
    return children.release();
  } else if (depth > 0) {
    depth--;
  }

  for (Node *child = InnerFirstChild(element); child != NULL;
       child = child->nextSibling()) {
    children->Append(BuildValueForNode(child, depth));
  }
  return children.release();
}

Node* DomAgentImpl::InnerFirstChild(Node* node) {
  if (node->isFrameOwnerElement()) {
    HTMLFrameOwnerElement* frame_owner =
        static_cast<HTMLFrameOwnerElement*>(node);
    Document* doc = frame_owner->contentDocument();
    StartListening(doc);
    return doc->firstChild();
  } else {
    return node->firstChild();
  }
}

int DomAgentImpl::InnerChildNodeCount(Node* node) {
  if (node->isFrameOwnerElement()) {
    return 1;
  } else {
    return node->childNodeCount();
  }
}

Element* DomAgentImpl::InnerParentElement(Node* node) {
  Element* element = node->parentElement();
  if (!element) {
    return node->ownerDocument()->ownerElement();
  }
  return element;
}
<|endoftext|>


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

#include "config.h"

#include "testing/gtest/include/gtest/gtest.h"

#include "base/compiler_specific.h"

MSVC_PUSH_WARNING_LEVEL(0);
#include "ResourceRequest.h"
#include "CString.h"
MSVC_POP_WARNING();
#undef LOG

#include "webkit/glue/webplugin_impl.h"

namespace {
  class WebPluginImplTest : public testing::Test {
  };
}

// These exist only to support the gTest assertion macros, and
// shouldn't be used in normal program code.
std::ostream& operator<<(std::ostream& out, const WebCore::String& str)
{
  return out << str.latin1().data();
}

// The Host functions for NPN_PostURL and NPN_PostURLNotify
// need to parse out some HTTP headers.  Make sure it works
// with the following tests

TEST(WebPluginImplTest, PostParserSimple) {
  // Test a simple case with headers & data
  char *ex1 = "foo: bar\nContent-length: 10\n\nabcdefghij";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ(0, request.httpHeaderField("Content-length").length());
  EXPECT_EQ("abcdefghij", request.httpBody()->flattenToString());
}

TEST(WebPluginImplTest, PostParserLongHeader) {
  // Test a simple case with long headers
  char *ex1 = "foo: 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\n\nabcdefghij";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ(100, request.httpHeaderField("foo").stripWhiteSpace().length());
}

TEST(WebPluginImplTest, PostParserManyHeaders) {
  // Test a simple case with long headers
  char *ex1 = "h1:h1\nh2:h2\nh3:h3\nh4:h4\nh5:h5\nh6:h6\nh7:h7\nh8:h8\nh9:h9\nh10:h10\n\nbody";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("h1", request.httpHeaderField("h1").stripWhiteSpace());
  EXPECT_EQ("h2", request.httpHeaderField("h2").stripWhiteSpace());
  EXPECT_EQ("h3", request.httpHeaderField("h3").stripWhiteSpace());
  EXPECT_EQ("h4", request.httpHeaderField("h4").stripWhiteSpace());
  EXPECT_EQ("h5", request.httpHeaderField("h5").stripWhiteSpace());
  EXPECT_EQ("h6", request.httpHeaderField("h6").stripWhiteSpace());
  EXPECT_EQ("h7", request.httpHeaderField("h7").stripWhiteSpace());
  EXPECT_EQ("h8", request.httpHeaderField("h8").stripWhiteSpace());
  EXPECT_EQ("h9", request.httpHeaderField("h9").stripWhiteSpace());
  EXPECT_EQ("h10", request.httpHeaderField("h10").stripWhiteSpace());
  WebCore::FormData *form_data = request.httpBody();
  WebCore::String string_data = form_data->flattenToString();
  EXPECT_EQ(string_data, "body");
}

TEST(WebPluginImplTest, PostParserDuplicateHeaders) {
  // Test a simple case with long headers
  // What value gets returned doesn't really matter.  It shouldn't error
  // out.
  char *ex1 = "h1:h1\nh1:h2\n\nbody";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
}

TEST(WebPluginImplTest, PostParserNoHeaders) {
  // Test a simple case with no headers but with data
  char *ex1 = "\nabcdefghij";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ(0, request.httpHeaderField("foo").length());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ(0, request.httpHeaderField("Content-length").length());
  EXPECT_EQ("abcdefghij", request.httpBody()->flattenToString());
}

TEST(WebPluginImplTest, PostParserNoBody) {
  // Test a simple case with headers and no body
  char *ex1 = "Foo:bar\n\n";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ(0, request.httpHeaderField("Content-length").length());
  EXPECT_EQ(0, request.httpBody()->flattenToString().length());
}

TEST(WebPluginImplTest, PostParserBodyWithNewLines) {
  // Test a simple case with headers and no body
  char *ex1 = "Foo:bar\n\n\n\nabcdefg\n\nabcdefg";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ(request.httpBody()->flattenToString(), "\n\nabcdefg\n\nabcdefg");
}

TEST(WebPluginImplTest, PostParserErrorNoBody) {
  // Test an error case with headers and no body
  char *ex1 = "Foo:bar\n";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(false, rv);
}

TEST(WebPluginImplTest, PostParserErrorEmpty) {
  // Test an error case with an empty string
  char *ex1 = "";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(false, rv);
}

TEST(WebPluginImplTest, PostParserEmptyName) {
  // Test an error case with an empty header name field
  char *ex1 = "foo:bar\n:blat\n\nbody";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ("body", request.httpBody()->flattenToString());
}

TEST(WebPluginImplTest, PostParserEmptyValue) {
  // Test an error case with an empty value field
  char *ex1 = "foo:bar\nbar:\n\nbody";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ("body", request.httpBody()->flattenToString());
}

TEST(WebPluginImplTest, PostParserCRLF) {
  // Test an error case with an empty value field
  char *ex1 = "foo: bar\r\nbar:\r\n\r\nbody\r\n\r\nbody2";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ("body\r\n\r\nbody2", request.httpBody()->flattenToString());
}

TEST(WebPluginImplTest, PostParserBodyWithBinaryData) {
  // Test a simple case with headers and binary data.
  char ex1[33] = "foo: bar\nContent-length: 10\n\n";
  unsigned int binary_data = 0xFFFFFFF0;
  memcpy(ex1 + strlen("foo: bar\nContent-length: 10\n\n"), &binary_data,
        sizeof(binary_data));

  WebCore::ResourceRequest request;
  bool rv = WebPluginImpl::SetPostData(&request, ex1,
                                      sizeof(ex1)/sizeof(ex1[0]));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ(0, request.httpHeaderField("Content-length").length());

  Vector<char> expected_data;
  request.httpBody()->flatten(expected_data);

  EXPECT_EQ(0xF0, (unsigned char)expected_data[0]);
  EXPECT_EQ(0xFF, (unsigned char)expected_data[1]);
  EXPECT_EQ(0xFF, (unsigned char)expected_data[2]);
  EXPECT_EQ(0xFF, (unsigned char)expected_data[3]);
}
<commit_msg>Temporarily disable failing unit tests. Opened issue 3542.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"

#include "testing/gtest/include/gtest/gtest.h"

#include "base/compiler_specific.h"

MSVC_PUSH_WARNING_LEVEL(0);
#include "ResourceRequest.h"
#include "CString.h"
MSVC_POP_WARNING();
#undef LOG

#include "webkit/glue/webplugin_impl.h"

namespace {
  class WebPluginImplTest : public testing::Test {
  };
}

// These exist only to support the gTest assertion macros, and
// shouldn't be used in normal program code.
std::ostream& operator<<(std::ostream& out, const WebCore::String& str)
{
  return out << str.latin1().data();
}

// The Host functions for NPN_PostURL and NPN_PostURLNotify
// need to parse out some HTTP headers.  Make sure it works
// with the following tests

TEST(WebPluginImplTest, PostParserSimple) {
  // Test a simple case with headers & data
  char *ex1 = "foo: bar\nContent-length: 10\n\nabcdefghij";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ(0, request.httpHeaderField("Content-length").length());
  EXPECT_EQ("abcdefghij", request.httpBody()->flattenToString());
}

TEST(WebPluginImplTest, PostParserLongHeader) {
  // Test a simple case with long headers
  char *ex1 = "foo: 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\n\nabcdefghij";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ(100, request.httpHeaderField("foo").stripWhiteSpace().length());
}

TEST(WebPluginImplTest, PostParserManyHeaders) {
  // Test a simple case with long headers
  char *ex1 = "h1:h1\nh2:h2\nh3:h3\nh4:h4\nh5:h5\nh6:h6\nh7:h7\nh8:h8\nh9:h9\nh10:h10\n\nbody";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("h1", request.httpHeaderField("h1").stripWhiteSpace());
  EXPECT_EQ("h2", request.httpHeaderField("h2").stripWhiteSpace());
  EXPECT_EQ("h3", request.httpHeaderField("h3").stripWhiteSpace());
  EXPECT_EQ("h4", request.httpHeaderField("h4").stripWhiteSpace());
  EXPECT_EQ("h5", request.httpHeaderField("h5").stripWhiteSpace());
  EXPECT_EQ("h6", request.httpHeaderField("h6").stripWhiteSpace());
  EXPECT_EQ("h7", request.httpHeaderField("h7").stripWhiteSpace());
  EXPECT_EQ("h8", request.httpHeaderField("h8").stripWhiteSpace());
  EXPECT_EQ("h9", request.httpHeaderField("h9").stripWhiteSpace());
  EXPECT_EQ("h10", request.httpHeaderField("h10").stripWhiteSpace());
  WebCore::FormData *form_data = request.httpBody();
  WebCore::String string_data = form_data->flattenToString();
  EXPECT_EQ(string_data, "body");
}

TEST(WebPluginImplTest, PostParserDuplicateHeaders) {
  // Test a simple case with long headers
  // What value gets returned doesn't really matter.  It shouldn't error
  // out.
  char *ex1 = "h1:h1\nh1:h2\n\nbody";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
}

TEST(WebPluginImplTest, PostParserNoHeaders) {
  // Test a simple case with no headers but with data
  char *ex1 = "\nabcdefghij";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ(0, request.httpHeaderField("foo").length());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ(0, request.httpHeaderField("Content-length").length());
  EXPECT_EQ("abcdefghij", request.httpBody()->flattenToString());
}

TEST(WebPluginImplTest, PostParserNoBody) {
  // Test a simple case with headers and no body
  char *ex1 = "Foo:bar\n\n";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ(0, request.httpHeaderField("Content-length").length());
  EXPECT_EQ(0, request.httpBody()->flattenToString().length());
}

TEST(WebPluginImplTest, PostParserBodyWithNewLines) {
  // Test a simple case with headers and no body
  char *ex1 = "Foo:bar\n\n\n\nabcdefg\n\nabcdefg";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ(request.httpBody()->flattenToString(), "\n\nabcdefg\n\nabcdefg");
}

// Bug 3542
TEST(WebPluginImplTest, DISABLED_PostParserErrorNoBody) {
  // Test an error case with headers and no body
  char *ex1 = "Foo:bar\n";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(false, rv);
}

// Bug 3542
TEST(WebPluginImplTest, DISABLED_PostParserErrorEmpty) {
  // Test an error case with an empty string
  char *ex1 = "";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(false, rv);
}

TEST(WebPluginImplTest, PostParserEmptyName) {
  // Test an error case with an empty header name field
  char *ex1 = "foo:bar\n:blat\n\nbody";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ("body", request.httpBody()->flattenToString());
}

TEST(WebPluginImplTest, PostParserEmptyValue) {
  // Test an error case with an empty value field
  char *ex1 = "foo:bar\nbar:\n\nbody";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ("body", request.httpBody()->flattenToString());
}

TEST(WebPluginImplTest, PostParserCRLF) {
  // Test an error case with an empty value field
  char *ex1 = "foo: bar\r\nbar:\r\n\r\nbody\r\n\r\nbody2";
  WebCore::ResourceRequest request;
  bool rv= WebPluginImpl::SetPostData(&request, ex1,
                                      static_cast<uint32>(strlen(ex1)));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ("body\r\n\r\nbody2", request.httpBody()->flattenToString());
}

TEST(WebPluginImplTest, PostParserBodyWithBinaryData) {
  // Test a simple case with headers and binary data.
  char ex1[33] = "foo: bar\nContent-length: 10\n\n";
  unsigned int binary_data = 0xFFFFFFF0;
  memcpy(ex1 + strlen("foo: bar\nContent-length: 10\n\n"), &binary_data,
        sizeof(binary_data));

  WebCore::ResourceRequest request;
  bool rv = WebPluginImpl::SetPostData(&request, ex1,
                                      sizeof(ex1)/sizeof(ex1[0]));
  EXPECT_EQ(true, rv);
  EXPECT_EQ("bar", request.httpHeaderField("foo").stripWhiteSpace());
  EXPECT_EQ(0, request.httpHeaderField("bar").length());
  EXPECT_EQ(0, request.httpHeaderField("Content-length").length());

  Vector<char> expected_data;
  request.httpBody()->flatten(expected_data);

  EXPECT_EQ(0xF0, (unsigned char)expected_data[0]);
  EXPECT_EQ(0xFF, (unsigned char)expected_data[1]);
  EXPECT_EQ(0xFF, (unsigned char)expected_data[2]);
  EXPECT_EQ(0xFF, (unsigned char)expected_data[3]);
}
<|endoftext|>


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

#include "v8_binding.h"

#include "AtomicString.h"
#include "CString.h"
#include "MathExtras.h"
#include "PlatformString.h"
#include "StringBuffer.h"

#include <v8.h>

namespace WebCore {

// WebCoreStringResource is a helper class for v8ExternalString. It is used
// to manage the life-cycle of the underlying buffer of the external string.
class WebCoreStringResource: public v8::String::ExternalStringResource {
 public:
  explicit WebCoreStringResource(const String& str)
      : impl_(str.impl()) {
    // We seem to be occasionally losing the backing string for external
    // strings: http://crbug.com/9746
    //
    // In order to verify that this is caused by a ref counting bug, we
    // artificially increase the ref count on the backing string until
    // we are done using it for external strings.
    //
    // TODO(ager): This is temporary and should be removed once we have
    // found the underlying cause of the problem.
    for (int i = 0; i < kArtificialRefIncrease; i++) {
      impl_.impl()->ref();
    }
  }

  virtual ~WebCoreStringResource() {
    // Remove the artificial ref counts added in the constructor.
    //
    // TODO(ager): This is temporary and should be removed once we have
    // found the underlying cause of the problem.
    for (int i = 0; i < kArtificialRefIncrease; i++) {
      impl_.impl()->deref();
    }
  }

  const uint16_t* data() const {
    return reinterpret_cast<const uint16_t*>(impl_.characters());
  }

  size_t length() const { return impl_.length(); }

  String webcore_string() { return impl_; }

 private:
  // The amount by which we artificially increase the reference count
  // of the backing string.
  //
  // TODO(ager): This is temporary and should be removed once we have
  // found the underlying cause of the problem.
  static const int kArtificialRefIncrease = 5;

  // A shallow copy of the string.
  // Keeps the string buffer alive until the V8 engine garbage collects it.
  String impl_;
};

String v8StringToWebCoreString(
    v8::Handle<v8::String> v8_str, bool externalize) {
  if (v8_str->IsExternal()) {
    WebCoreStringResource* str_resource = static_cast<WebCoreStringResource*>(
        v8_str->GetExternalStringResource());
    return str_resource->webcore_string();
  }

  int length = v8_str->Length();
  if (length == 0) {
    // Avoid trying to morph empty strings, as they do not have enough room to
    // contain the external reference.
    return "";
  }

  // Copy the characters from the v8::String into a WebCore::String and allocate
  // an external resource which will be attached to the v8::String.
  String result;
  const int kStringSizeToCopy = 256;
  if (length < kStringSizeToCopy) {
    uint16_t buffer[kStringSizeToCopy];
    v8_str->Write(buffer, 0, length);
    result = StringImpl::create(reinterpret_cast<UChar*>(buffer), length);
  } else {
    StringBuffer buf(length);
    v8_str->Write(reinterpret_cast<uint16_t*>(buf.characters()), 0, length);
    result = String::adopt(buf);
  }

//
//  TODO(mbelshe): Disable string morphing because it causes mystery
//     perf regressions on intl1 and intl2 page cyclers.  It works fine
//     on machines other than the buildbots.

  if (externalize) {
    WebCoreStringResource* resource = new WebCoreStringResource(result);
    if (!v8_str->MakeExternal(resource)) {
      // In case of a failure delete the external resource as it was not used.
      delete resource;
    }
  }
  return result;
}

String v8ValueToWebCoreString(v8::Handle<v8::Value> obj) {
  v8::Handle<v8::String> v8_str;
  if (obj->IsString()) {
    v8_str = v8::Handle<v8::String>::Cast(obj);
    return v8StringToWebCoreString(v8_str, true);
  } else {
    v8::TryCatch block;
    v8_str = obj->ToString();
    if (v8_str.IsEmpty())
      return "";
  }
  return v8StringToWebCoreString(v8_str, false);
}

AtomicString v8StringToAtomicWebCoreString(v8::Handle<v8::String> v8_str) {
  String str = v8StringToWebCoreString(v8_str, true);
  return AtomicString(str);
}

AtomicString v8ValueToAtomicWebCoreString(v8::Handle<v8::Value> v8_str) {
  String str = v8ValueToWebCoreString(v8_str);
  return AtomicString(str);
}

v8::Handle<v8::String> v8String(const String& str) {
  if (!str.length())
    return v8::String::Empty();
  return v8::String::NewExternal(new WebCoreStringResource(str));
}

v8::Local<v8::String> v8ExternalString(const String& str) {
  if (!str.length())
    return v8::String::Empty();
  return v8::String::NewExternal(new WebCoreStringResource(str));
}

}  // namespace WebCore
<commit_msg>Undo the reference counting experiment.  The crashes are still happening, so it seems unlikely that this is a reference counting problem.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "v8_binding.h"

#include "AtomicString.h"
#include "CString.h"
#include "MathExtras.h"
#include "PlatformString.h"
#include "StringBuffer.h"

#include <v8.h>

namespace WebCore {

// WebCoreStringResource is a helper class for v8ExternalString. It is used
// to manage the life-cycle of the underlying buffer of the external string.
class WebCoreStringResource: public v8::String::ExternalStringResource {
 public:
  explicit WebCoreStringResource(const String& str)
      : impl_(str.impl()) { }

  virtual ~WebCoreStringResource() {}

  const uint16_t* data() const {
    return reinterpret_cast<const uint16_t*>(impl_.characters());
  }

  size_t length() const { return impl_.length(); }

  String webcore_string() { return impl_; }

 private:
  // A shallow copy of the string.
  // Keeps the string buffer alive until the V8 engine garbage collects it.
  String impl_;
};

String v8StringToWebCoreString(
    v8::Handle<v8::String> v8_str, bool externalize) {
  if (v8_str->IsExternal()) {
    WebCoreStringResource* str_resource = static_cast<WebCoreStringResource*>(
        v8_str->GetExternalStringResource());
    return str_resource->webcore_string();
  }

  int length = v8_str->Length();
  if (length == 0) {
    // Avoid trying to morph empty strings, as they do not have enough room to
    // contain the external reference.
    return "";
  }

  // Copy the characters from the v8::String into a WebCore::String and allocate
  // an external resource which will be attached to the v8::String.
  String result;
  const int kStringSizeToCopy = 256;
  if (length < kStringSizeToCopy) {
    uint16_t buffer[kStringSizeToCopy];
    v8_str->Write(buffer, 0, length);
    result = StringImpl::create(reinterpret_cast<UChar*>(buffer), length);
  } else {
    StringBuffer buf(length);
    v8_str->Write(reinterpret_cast<uint16_t*>(buf.characters()), 0, length);
    result = String::adopt(buf);
  }

//
//  TODO(mbelshe): Disable string morphing because it causes mystery
//     perf regressions on intl1 and intl2 page cyclers.  It works fine
//     on machines other than the buildbots.

  if (externalize) {
    WebCoreStringResource* resource = new WebCoreStringResource(result);
    if (!v8_str->MakeExternal(resource)) {
      // In case of a failure delete the external resource as it was not used.
      delete resource;
    }
  }
  return result;
}

String v8ValueToWebCoreString(v8::Handle<v8::Value> obj) {
  v8::Handle<v8::String> v8_str;
  if (obj->IsString()) {
    v8_str = v8::Handle<v8::String>::Cast(obj);
    return v8StringToWebCoreString(v8_str, true);
  } else {
    v8::TryCatch block;
    v8_str = obj->ToString();
    if (v8_str.IsEmpty())
      return "";
  }
  return v8StringToWebCoreString(v8_str, false);
}

AtomicString v8StringToAtomicWebCoreString(v8::Handle<v8::String> v8_str) {
  String str = v8StringToWebCoreString(v8_str, true);
  return AtomicString(str);
}

AtomicString v8ValueToAtomicWebCoreString(v8::Handle<v8::Value> v8_str) {
  String str = v8ValueToWebCoreString(v8_str);
  return AtomicString(str);
}

v8::Handle<v8::String> v8String(const String& str) {
  if (!str.length())
    return v8::String::Empty();
  return v8::String::NewExternal(new WebCoreStringResource(str));
}

v8::Local<v8::String> v8ExternalString(const String& str) {
  if (!str.length())
    return v8::String::Empty();
  return v8::String::NewExternal(new WebCoreStringResource(str));
}

}  // namespace WebCore
<|endoftext|>


<commit_before>/*
 * Second-Order Allpass Filter Object for Jamoma DSP
 * Copyright © 2010, Tim Place
 *
 * License: This code is licensed under the terms of the "New BSD License"
 * http://creativecommons.org/licenses/BSD/
 */

#include "TTAllpass2b.h"

#define thisTTClass			TTAllpass2b
#define thisTTClassName		"allpass.2b"
#define thisTTClassTags		"audio, processor, filter, allpass"

#ifdef TT_PLATFORM_WIN
#include <Algorithm>
#endif

TT_AUDIO_CONSTRUCTOR,
	mC1(0),
	mC2(0)
{
	TTUInt16 initialMaxNumChannels = arguments;

	addAttribute(C1, kTypeFloat64);
	addAttribute(C2, kTypeFloat64);

	addMessage(Clear);
	addMessageWithArgument(updateMaxNumChannels);

	setAttributeValue(TT("MaxNumChannels"),	initialMaxNumChannels);
	setProcessMethod(processAudio);
}

TTAllpass2b::~TTAllpass2b()
{
	;
}

TTErr TTAllpass2b::updateMaxNumChannels(const TTValue& oldMaxNumChannels)
{
	mX1.resize(maxNumChannels);
	mX2.resize(maxNumChannels);
	mX3.resize(maxNumChannels);
	mX4.resize(maxNumChannels);
	mY1.resize(maxNumChannels);
	mY2.resize(maxNumChannels);
	mY3.resize(maxNumChannels);
	mY4.resize(maxNumChannels);
	Clear();
	return kTTErrNone;
}

TTErr TTAllpass2b::Clear()
{
	mX1.assign(maxNumChannels, 0.0);
	mX2.assign(maxNumChannels, 0.0);
	mX3.assign(maxNumChannels, 0.0);
	mX4.assign(maxNumChannels, 0.0);
	mY1.assign(maxNumChannels, 0.0);
	mY2.assign(maxNumChannels, 0.0);
	mY3.assign(maxNumChannels, 0.0);
	mY4.assign(maxNumChannels, 0.0);
	return kTTErrNone;
}

TTErr TTAllpass2b::calculateValue(const TTFloat64& x, TTFloat64& y, TTPtrSizedInt channel)
{
	TTFloat64 w1 = mC1 * (mX2[channel] + mY2[channel]);
	TTFloat64 w2 = mC2 * (x + mY4[channel]);

	y = w1 + w2 + mX2[channel];

	TTZeroDenormal(y);

	mX4[channel] = mX3[channel];
	mY4[channel] = mY3[channel];
	mX3[channel] = mX2[channel];
	mY3[channel] = mY2[channel];
	mX2[channel] = mX1[channel];
	mY2[channel] = mY1[channel];
	mX1[channel] = x;
	mY1[channel] = y;
	return kTTErrNone;
}

TTErr TTAllpass2b::processAudio(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
{
	TT_WRAP_CALCULATE_METHOD(calculateValue);
}

<commit_msg>TTAllpass2b: fixed problems with incorrect sign-flips (or non-flips) in the performance calculation.<commit_after>/*
 * Second-Order Allpass Filter Object for Jamoma DSP
 * Copyright © 2010, Tim Place
 *
 * License: This code is licensed under the terms of the "New BSD License"
 * http://creativecommons.org/licenses/BSD/
 */

#include "TTAllpass2b.h"

#define thisTTClass			TTAllpass2b
#define thisTTClassName		"allpass.2b"
#define thisTTClassTags		"audio, processor, filter, allpass"

#ifdef TT_PLATFORM_WIN
#include <Algorithm>
#endif

TT_AUDIO_CONSTRUCTOR,
	mC1(0),
	mC2(0)
{
	TTUInt16 initialMaxNumChannels = arguments;

	addAttribute(C1, kTypeFloat64);
	addAttribute(C2, kTypeFloat64);

	addMessage(Clear);
	addMessageWithArgument(updateMaxNumChannels);

	setAttributeValue(TT("MaxNumChannels"),	initialMaxNumChannels);
	setProcessMethod(processAudio);
}

TTAllpass2b::~TTAllpass2b()
{
	;
}

TTErr TTAllpass2b::updateMaxNumChannels(const TTValue& oldMaxNumChannels)
{
	mX1.resize(maxNumChannels);
	mX2.resize(maxNumChannels);
	mX3.resize(maxNumChannels);
	mX4.resize(maxNumChannels);
	mY1.resize(maxNumChannels);
	mY2.resize(maxNumChannels);
	mY3.resize(maxNumChannels);
	mY4.resize(maxNumChannels);
	Clear();
	return kTTErrNone;
}

TTErr TTAllpass2b::Clear()
{
	mX1.assign(maxNumChannels, 0.0);
	mX2.assign(maxNumChannels, 0.0);
	mX3.assign(maxNumChannels, 0.0);
	mX4.assign(maxNumChannels, 0.0);
	mY1.assign(maxNumChannels, 0.0);
	mY2.assign(maxNumChannels, 0.0);
	mY3.assign(maxNumChannels, 0.0);
	mY4.assign(maxNumChannels, 0.0);
	return kTTErrNone;
}

TTErr TTAllpass2b::calculateValue(const TTFloat64& x, TTFloat64& y, TTPtrSizedInt channel)
{
	TTFloat64 w1 = mC1 * (mX2[channel] - mY2[channel]);
	TTFloat64 w2 = mC2 * (x - mY4[channel]);

	y = w1 + w2 + mX4[channel];

	TTZeroDenormal(y);

	mX4[channel] = mX3[channel];
	mY4[channel] = mY3[channel];
	mX3[channel] = mX2[channel];
	mY3[channel] = mY2[channel];
	mX2[channel] = mX1[channel];
	mY2[channel] = mY1[channel];
	mX1[channel] = x;
	mY1[channel] = y;
	return kTTErrNone;
}

TTErr TTAllpass2b::processAudio(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
{
	TT_WRAP_CALCULATE_METHOD(calculateValue);
}

<|endoftext|>


<commit_before>// exponent.cpp: test suite runner for exponent (exp, exp2, exp10) functions
//
// Copyright (C) 2017-2021 Stillwater Supercomputing, Inc.
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
#include <universal/utility/directives.hpp>

// when you define cfloat_VERBOSE_OUTPUT the code will print intermediate results for selected arithmetic operations
//#define CFLOAT_VERBOSE_OUTPUT
#define CFLOAT_TRACE_POW

// use default number system library configuration
#include <universal/number/cfloat/cfloat>
#include <universal/verification/cfloat_math_test_suite.hpp>

// Background: http://numbers.computation.free.fr/Constants/E/e.html
//
// generate digits of Euler's number
void GenerateEulersNumber() {
	int N = 9009, a[9009], x = 0;
	for (int n = N - 1; n > 0; --n) {
		a[n] = 1;
	}
	a[1] = 2;
	while (N > 9) {
		int n = N--;
		while (--n) {
			a[n] = x % n;
			x = 10 * a[n - 1] + x / n;
		}
		std::cout << x;
	}
	std::cout << std::endl;
}

// generate specific test case that you can trace with the trace conditions in cfloat.h
// for most bugs they are traceable with _trace_conversion and _trace_add
template<size_t nbits, size_t es, typename Ty>
void GenerateTestCase(Ty a) {
	Ty ref;
	sw::universal::cfloat<nbits, es> pa, pref, pexp;
	pa = a;
	ref = std::exp(a);
	pref = ref;
	pexp = sw::universal::exp(pa);
	std::cout << std::setprecision(nbits - 2);
	std::cout << std::setw(nbits) << a << " -> exp(" << a << ") = " << std::setw(nbits) << ref << std::endl;
	std::cout << to_binary(pa) << " -> exp( " << pa << ") = " << to_binary(pexp) << " (reference: " << to_binary(pref) << ")   ";
	std::cout << (pref == pexp ? "PASS" : "FAIL") << std::endl << std::endl;
	std::cout << std::setprecision(5);
}

#define MANUAL_TESTING 0
#define STRESS_TESTING 0
#define GENERATE_EXPONENT_TABLES 0

int main()
try {
	using namespace std;
	using namespace sw::universal;

//	GenerateEulersNumber();

	bool bReportIndividualTestCases = false;
	int nrOfFailedTestCases = 0;

	std::string tag = "cfloat exp() failed: ";

#if MANUAL_TESTING
	// generate individual testcases to hand trace/debug
	GenerateTestCase<16, 1, float>(4.0f);

#if GENERATE_EXPONENT_TABLES

	GenerateExponentTable<5, 1>();
	GenerateExponentTable<5, 2>();
	GenerateExponentTable<6, 1>();
	GenerateExponentTable<6, 2>();
	GenerateExponentTable<6, 3>();
#endif

	cfloat<8, 2> a, aexp2, aref;
	a.setbits(0xFF);
	aexp2 = sw::universal::exp2(a);
	// generate reference
	double da = double(a);
	double dref = std::exp2(da);
	aref = dref;
	cout << to_binary(aref) << " : " << aref << " : " << to_binary(dref) << endl;
	cout << to_binary(ieee754_parameter<double>::fmask) << endl;
	cout << to_binary(ieee754_parameter<double>::snanmask) << endl;

	cout << endl;

	// manual exhaustive test
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat<8, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<8,2>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<8, 4, uint8_t> >(bReportIndividualTestCases), "cfloat<8,4>", "exp2");

#else

	cout << "cfloat exponential function validation" << endl;

	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat< 8, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<8,2>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat< 8, 3, uint8_t> >(bReportIndividualTestCases), "cfloat<8,3>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat< 9, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<9,2>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat<10, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<10,2>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat<10, 3, uint8_t> >(bReportIndividualTestCases), "cfloat<10,3>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat<12, 4, uint8_t> >(bReportIndividualTestCases), "cfloat<12,4>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat<16, 5, uint8_t> >(bReportIndividualTestCases), "cfloat<16,5>", "exp");

	// base-2 exponent testing
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<8, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<8,2>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<8, 3, uint8_t> >(bReportIndividualTestCases), "cfloat<8,3>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<9, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<9,2>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<10, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<10,2>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<10, 3, uint8_t> >(bReportIndividualTestCases), "cfloat<10,3>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<12, 4, uint8_t> >(bReportIndividualTestCases), "cfloat<12,4>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<16, 5, uint8_t> >(bReportIndividualTestCases), "cfloat<16,5>", "exp2");

#if STRESS_TESTING

#endif  // STRESS_TESTING

#endif  // MANUAL_TESTING

	return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
catch (char const* msg) {
	std::cerr << msg << std::endl;
	return EXIT_FAILURE;
}
catch (const sw::universal::cfloat_arithmetic_exception& err) {
	std::cerr << "Uncaught cfloat arithmetic exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const sw::universal::cfloat_quire_exception& err) {
	std::cerr << "Uncaught quire exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const sw::universal::cfloat_internal_exception& err) {
	std::cerr << "Uncaught cfloat internal exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const std::runtime_error& err) {
	std::cerr << "Uncaught runtime exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (...) {
	std::cerr << "Caught unknown exception" << std::endl;
	return EXIT_FAILURE;
}
<commit_msg>removing Euler's number from cfloat exponent test<commit_after>// exponent.cpp: test suite runner for exponent (exp, exp2, exp10) functions
//
// Copyright (C) 2017-2021 Stillwater Supercomputing, Inc.
//
// This file is part of the universal numbers project, which is released under an MIT Open Source license.
#include <universal/utility/directives.hpp>

// when you define cfloat_VERBOSE_OUTPUT the code will print intermediate results for selected arithmetic operations
//#define CFLOAT_VERBOSE_OUTPUT
#define CFLOAT_TRACE_POW

// use default number system library configuration
#include <universal/number/cfloat/cfloat>
#include <universal/verification/cfloat_math_test_suite.hpp>

// Background: http://numbers.computation.free.fr/Constants/E/e.html
//
// generate digits of Euler's number
void GenerateEulersNumber() {
	int N = 9009, a[9009], x = 0;
	for (int n = N - 1; n > 0; --n) {
		a[n] = 1;
	}
	a[1] = 2;
	while (N > 9) {
		int n = N--;
		while (--n) {
			a[n] = x % n;
			x = 10 * a[n - 1] + x / n;
		}
		std::cout << x;
	}
	std::cout << std::endl;
}

// generate specific test case that you can trace with the trace conditions in cfloat.h
// for most bugs they are traceable with _trace_conversion and _trace_add
template<size_t nbits, size_t es, typename Ty>
void GenerateTestCase(Ty a) {
	Ty ref;
	sw::universal::cfloat<nbits, es> pa, pref, pexp;
	pa = a;
	ref = std::exp(a);
	pref = ref;
	pexp = sw::universal::exp(pa);
	std::cout << std::setprecision(nbits - 2);
	std::cout << std::setw(nbits) << a << " -> exp(" << a << ") = " << std::setw(nbits) << ref << std::endl;
	std::cout << to_binary(pa) << " -> exp( " << pa << ") = " << to_binary(pexp) << " (reference: " << to_binary(pref) << ")   ";
	std::cout << (pref == pexp ? "PASS" : "FAIL") << std::endl << std::endl;
	std::cout << std::setprecision(5);
}

#define MANUAL_TESTING 0
#define STRESS_TESTING 0
#define GENERATE_EXPONENT_TABLES 0

int main()
try {
	using namespace std;
	using namespace sw::universal;

//	GenerateEulersNumber();  // 9000 digits of e

	bool bReportIndividualTestCases = false;
	int nrOfFailedTestCases = 0;

	std::string tag = "cfloat exp() failed: ";

#if MANUAL_TESTING
	// generate individual testcases to hand trace/debug
	GenerateTestCase<16, 1, float>(4.0f);

#if GENERATE_EXPONENT_TABLES

	GenerateExponentTable<5, 1>();
	GenerateExponentTable<5, 2>();
	GenerateExponentTable<6, 1>();
	GenerateExponentTable<6, 2>();
	GenerateExponentTable<6, 3>();
#endif

	cfloat<8, 2> a, aexp2, aref;
	a.setbits(0xFF);
	aexp2 = sw::universal::exp2(a);
	// generate reference
	double da = double(a);
	double dref = std::exp2(da);
	aref = dref;
	cout << to_binary(aref) << " : " << aref << " : " << to_binary(dref) << endl;
	cout << to_binary(ieee754_parameter<double>::fmask) << endl;
	cout << to_binary(ieee754_parameter<double>::snanmask) << endl;

	cout << endl;

	// manual exhaustive test
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat<8, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<8,2>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<8, 4, uint8_t> >(bReportIndividualTestCases), "cfloat<8,4>", "exp2");

#else

	cout << "cfloat exponential function validation" << endl;

	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat< 8, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<8,2>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat< 8, 3, uint8_t> >(bReportIndividualTestCases), "cfloat<8,3>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat< 9, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<9,2>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat<10, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<10,2>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat<10, 3, uint8_t> >(bReportIndividualTestCases), "cfloat<10,3>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat<12, 4, uint8_t> >(bReportIndividualTestCases), "cfloat<12,4>", "exp");
	nrOfFailedTestCases += ReportTestResult(VerifyExp< cfloat<16, 5, uint8_t> >(bReportIndividualTestCases), "cfloat<16,5>", "exp");

	// base-2 exponent testing
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<8, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<8,2>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<8, 3, uint8_t> >(bReportIndividualTestCases), "cfloat<8,3>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<9, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<9,2>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<10, 2, uint8_t> >(bReportIndividualTestCases), "cfloat<10,2>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<10, 3, uint8_t> >(bReportIndividualTestCases), "cfloat<10,3>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<12, 4, uint8_t> >(bReportIndividualTestCases), "cfloat<12,4>", "exp2");
	nrOfFailedTestCases += ReportTestResult(VerifyExp2< cfloat<16, 5, uint8_t> >(bReportIndividualTestCases), "cfloat<16,5>", "exp2");

#if STRESS_TESTING

#endif  // STRESS_TESTING

#endif  // MANUAL_TESTING

	return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
catch (char const* msg) {
	std::cerr << msg << std::endl;
	return EXIT_FAILURE;
}
catch (const sw::universal::cfloat_arithmetic_exception& err) {
	std::cerr << "Uncaught cfloat arithmetic exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const sw::universal::cfloat_quire_exception& err) {
	std::cerr << "Uncaught quire exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const sw::universal::cfloat_internal_exception& err) {
	std::cerr << "Uncaught cfloat internal exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (const std::runtime_error& err) {
	std::cerr << "Uncaught runtime exception: " << err.what() << std::endl;
	return EXIT_FAILURE;
}
catch (...) {
	std::cerr << "Caught unknown exception" << std::endl;
	return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdexcept>
#include <fstream>
#include <xorg/gtest/xorg-gtest.h>

#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>

#include "xit-server-input-test.h"
#include "xit-event.h"
#include "helpers.h"

/* no implementation, this class only exists for better test names */
class LegacyInputDriverTest : public SimpleInputDriverTest {};

static int count_devices(Display *dpy) {
    int ndevices;
    XIDeviceInfo *info;

    info = XIQueryDevice(dpy, XIAllDevices, &ndevices);
    XIFreeDeviceInfo(info);
    return ndevices;
}

TEST_P(LegacyInputDriverTest, InputDeviceSectionSimple)
{
    std::string param;

    param = GetParam();

    int expected_devices;

    /* apparently when the acecad driver is loaded neither mouse nor kbd
     * loads - probably a bug but that's current behaviour on RHEL 6.3 */
    if (param.compare("acecad") == 0)
        expected_devices = 4;
    else {
        expected_devices = 6;
	/* xserver git 1357cd7251 , no <default pointer> */
	if (server.GetVersion().compare("1.11.0") > 0)
		expected_devices--;
    }

    ASSERT_EQ(count_devices(Display()), expected_devices);

    /* No joke, they all fail. some fail for missing Device option, others
     * for not finding the device, etc. */
    ASSERT_EQ(FindInputDeviceByName(Display(), "--device--"), 0);
}

INSTANTIATE_TEST_CASE_P(, LegacyInputDriverTest,
        ::testing::Values("acecad", "aiptek", "elographics",
                          "fpit", "hyperpen",  "mutouch",
                          "penmount"));

/**
 * Void input driver test class
 */
class VoidTest : public XITServerInputTest {
public:
    /**
     * Initialize an xorg.conf with a single CorePointer void device.
     */
    virtual void SetUpConfigAndLog() {

        config.AddDefaultScreenWithDriver();
        config.AddInputSection("void", "--device--", "Option \"CorePointer\" \"on\"\n");
        config.WriteConfig();
        server.SetOption("-config", config.GetPath());
    }
};

TEST_F(VoidTest, InputDeviceSectionSimple)
{
    ASSERT_EQ(FindInputDeviceByName(Display(), "--device--"), 1);
}

/***********************************************************************
 *                                                                     *
 *                               ACECAD                                *
 *                                                                     *
 ***********************************************************************/
TEST(AcecadTest, InputDeviceSectionWithOptionDevice)
{
    XOrgConfig config;
    XITServer server;

    config.AddInputSection("acecad", "--device--",
                           "Option \"CorePointer\" \"on\"\n"
                           "Option \"Device\" \"/dev/input/event0\"\n");
    config.AddDefaultScreenWithDriver();
    server.Start(config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
    int major = 2;
    int minor = 0;
    ASSERT_EQ(Success, XIQueryVersion(dpy, &major, &minor));

    int ndevices;
    XIDeviceInfo *info;

    info = XIQueryDevice(dpy, XIAllDevices, &ndevices);
    XIFreeDeviceInfo(info);

    /* VCP, VCK, xtest, mouse, keyboard, acecad */
    int expected_devices = 7;

    /* xserver git 1357cd7251 , no <default pointer> */
    if (server.GetVersion().compare("1.11.0") > 0)
	    expected_devices--;

    ASSERT_EQ(count_devices(dpy), expected_devices);
    ASSERT_EQ(FindInputDeviceByName(dpy, "--device--"), 1);

    config.RemoveConfig();
    server.RemoveLogFile();
}

/***********************************************************************
 *                                                                     *
 *                               AIPTEK                                *
 *                                                                     *
 ***********************************************************************/

class AiptekTest : public XITServerInputTest,
                   public ::testing::WithParamInterface<std::string> {
    virtual void SetUpConfigAndLog() {

        std::string type = GetParam();

        config.AddInputSection("aiptek", "--device--",
                "Option \"CorePointer\" \"on\"\n"
                "Option \"Device\" \"/dev/input/event0\"\n"
                "Option \"Type\" \"" + type + "\"");
        config.AddDefaultScreenWithDriver();
        config.WriteConfig();
    }
};

TEST_P(AiptekTest, InputDeviceSectionWithType)
{
    ::Display *dpy = Display();

    /* VCP, VCK, xtest, mouse, keyboard, aiptek */
    int expected_devices = 7;

    /* xserver git 1357cd7251, no <default pointer> */
    if (server.GetVersion().compare("1.11.0") > 0)
	    expected_devices--;

    ASSERT_EQ(count_devices(dpy), expected_devices);
    ASSERT_EQ(FindInputDeviceByName(dpy, "--device--"), 1);

    config.RemoveConfig();
    server.RemoveLogFile();
}

INSTANTIATE_TEST_CASE_P(, AiptekTest,
                        ::testing::Values("stylus", "cursor", "eraser"));

/***********************************************************************
 *                                                                     *
 *                            ELOGRAPHICS                              *
 *                                                                     *
 ***********************************************************************/
TEST(ElographicsTest, InputDeviceSectionWithOptionDevice)
{
    XOrgConfig config;
    XITServer server;

    config.AddInputSection("elographics", "--device--",
                           "Option \"CorePointer\" \"on\"\n"
                           "Option \"Device\" \"/dev/input/event0\"\n");
    config.AddDefaultScreenWithDriver();
    server.Start(config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
    int major = 2;
    int minor = 0;
    ASSERT_EQ(Success, XIQueryVersion(dpy, &major, &minor));

    /* VCP, VCK, xtest, mouse, keyboard, aiptek */
    int expected_devices = 7;

    /* xserver git 1357cd7251, no <default pointer> */
    if (server.GetVersion().compare("1.11.0") > 0)
	    expected_devices--;

    ASSERT_EQ(count_devices(dpy), expected_devices);

    int deviceid;
    if (FindInputDeviceByName(dpy, "--device--", &deviceid) != 1) {
        SCOPED_TRACE("\n"
                     "	Elographics device '--device--' not found.\n"
                     "	Maybe this is elographics < 1.4.\n"
                     "	Checking for TOUCHSCREEN instead, see git commit\n"
                     "	xf86-input-elographics-1.3.0-1-g55f337f");
        ASSERT_EQ(FindInputDeviceByName(dpy, "TOUCHSCREEN", &deviceid), 1);
    } else
        ASSERT_EQ(FindInputDeviceByName(dpy, "--device--", &deviceid), 1);

    int ndevices;
    XIDeviceInfo *info = XIQueryDevice(dpy, deviceid, &ndevices);
    ASSERT_EQ(ndevices, 1);

    ASSERT_EQ(info->use, XISlavePointer) << "https://bugs.freedesktop.org/show_bug.cgi?id=40870";
    ASSERT_EQ(info->attachment, VIRTUAL_CORE_POINTER_ID);

    XIFreeDeviceInfo(info);

    config.RemoveConfig();
    server.RemoveLogFile();
}

/* Create a named pipe that pumps out elographics 10-byte packets */
struct elo_packet {
    unsigned char header;               /* always 'U' */
    unsigned char expected;             /* always 'T' (touch packet) in this program */
    unsigned char has_pressure;         /* 0x80 if pressure is present, 0 otherwise */
    unsigned char x_lo;
    unsigned char x_hi;
    unsigned char y_lo;
    unsigned char y_hi;
    unsigned char pressure_hi;
    unsigned char pressure_lo;
    unsigned char checksum;             /* 0x88 + bytes[1..8] */
};

static void elo_init_packet(struct elo_packet *packet, short x, short y, short pressure)
{
    int i;

    packet->header = 'U';
    packet->expected = 'T';
    packet->has_pressure = 0x80;
    packet->x_lo = x & 0xFF;
    packet->x_hi = (x & 0xFF00) >> 8;
    packet->y_lo = y & 0xFF;
    packet->y_hi = (y & 0xFF00) >> 8;
    packet->pressure_lo = pressure & 0xFF;
    packet->pressure_hi = (pressure & 0xFF00) >> 8;

    packet->checksum = 0xaa;

    for (i = 0; i < 9; i++)
        packet->checksum += *(((unsigned char*)packet) + i);
}

static void elo_sighandler(int sig)
{
    unlink("/tmp/elographics.pipe");
    exit(0);
}

static void elo_sighandler_pipe(int sig)
{
}

int elo_simulate(void)
{
    int i;
    int rc, fd;
    struct elo_packet packet;
    const char *path = "/tmp/elographics.pipe";

    rc = mkfifo(path, 0x755);
    if (rc == -1) {
        perror("Failed to create pipe:");
        return 1;
    }

    signal(SIGINT, elo_sighandler);
    signal(SIGTERM, elo_sighandler);
    signal(SIGPIPE, elo_sighandler_pipe);

    fd = open(path, O_WRONLY);
    if (fd == -1) {
        perror("Failed to open pipe:");
        rc = 1;
        goto out;
    }

    for (i = 0; i < 30; i++) {
        elo_init_packet(&packet, 100 * i, 10, 10);
        rc = write(fd, &packet, sizeof(packet));
        if (rc == -1 && errno == SIGPIPE)
            i--;
        usleep(50000);
    }

    if (i == 30)
        rc = 0;

out:
    unlink(path);

    return rc;
}

TEST(ElographicsTest, StylusMovement)
{
    XOrgConfig config;
    XITServer server;

    config.AddInputSection("elographics", "--device--",
                           "Option \"CorePointer\" \"on\"\n"
                           "Option \"DebugLevel\" \"10\"\n"
                           "Option \"Model\" \"Sunit dSeries\"" /* does not send packets to device*/
                           "Option \"Device\" \"/tmp/elographics.pipe\"\n");
    config.AddDefaultScreenWithDriver();

    /* fork here */
    pid_t pid = fork();
    if (pid == 0) { /* child */
#ifdef __linux
        prctl(PR_SET_PDEATHSIG, SIGTERM);
#endif
        if (elo_simulate())
            throw new std::runtime_error("simulation failed\n");
        exit(0);
    }

    /* parent */
    server.Start(config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
    int major = 2;
    int minor = 0;
    ASSERT_EQ(Success, XIQueryVersion(dpy, &major, &minor));

    XSelectInput(dpy, DefaultRootWindow(dpy), PointerMotionMask);
    XSync(dpy, False);

    if (FindInputDeviceByName(dpy, "--device--") != 1) {
        SCOPED_TRACE("\n"
                     "	Elographics device '--device--' not found.\n"
                     "	Maybe this is elographics < 1.4.\n"
                     "	Checking for TOUCHSCREEN instead, see git commit\n"
                     "	xf86-input-elographics-1.3.0-1-g55f337f");
        ASSERT_EQ(FindInputDeviceByName(dpy, "TOUCHSCREEN"), 1);
    } else
        ASSERT_EQ(FindInputDeviceByName(dpy, "--device--"), 1);

    ASSERT_EVENT(XEvent, first, dpy, MotionNotify);
    ASSERT_EVENT(XEvent, second, dpy, MotionNotify);
    ASSERT_LT(first->xmotion.x_root, second->xmotion.x_root);

    int status;
    wait(&status);

    config.RemoveConfig();
    server.RemoveLogFile();
}

int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<commit_msg>input/legacy-drivers: move forking for the elographics driver into helper<commit_after>#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdexcept>
#include <fstream>
#include <xorg/gtest/xorg-gtest.h>

#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <X11/Xlib.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>

#include "xit-server-input-test.h"
#include "xit-event.h"
#include "helpers.h"

/* no implementation, this class only exists for better test names */
class LegacyInputDriverTest : public SimpleInputDriverTest {};

static int count_devices(Display *dpy) {
    int ndevices;
    XIDeviceInfo *info;

    info = XIQueryDevice(dpy, XIAllDevices, &ndevices);
    XIFreeDeviceInfo(info);
    return ndevices;
}

TEST_P(LegacyInputDriverTest, InputDeviceSectionSimple)
{
    std::string param;

    param = GetParam();

    int expected_devices;

    /* apparently when the acecad driver is loaded neither mouse nor kbd
     * loads - probably a bug but that's current behaviour on RHEL 6.3 */
    if (param.compare("acecad") == 0)
        expected_devices = 4;
    else {
        expected_devices = 6;
	/* xserver git 1357cd7251 , no <default pointer> */
	if (server.GetVersion().compare("1.11.0") > 0)
		expected_devices--;
    }

    ASSERT_EQ(count_devices(Display()), expected_devices);

    /* No joke, they all fail. some fail for missing Device option, others
     * for not finding the device, etc. */
    ASSERT_EQ(FindInputDeviceByName(Display(), "--device--"), 0);
}

INSTANTIATE_TEST_CASE_P(, LegacyInputDriverTest,
        ::testing::Values("acecad", "aiptek", "elographics",
                          "fpit", "hyperpen",  "mutouch",
                          "penmount"));

/**
 * Void input driver test class
 */
class VoidTest : public XITServerInputTest {
public:
    /**
     * Initialize an xorg.conf with a single CorePointer void device.
     */
    virtual void SetUpConfigAndLog() {

        config.AddDefaultScreenWithDriver();
        config.AddInputSection("void", "--device--", "Option \"CorePointer\" \"on\"\n");
        config.WriteConfig();
        server.SetOption("-config", config.GetPath());
    }
};

TEST_F(VoidTest, InputDeviceSectionSimple)
{
    ASSERT_EQ(FindInputDeviceByName(Display(), "--device--"), 1);
}

/***********************************************************************
 *                                                                     *
 *                               ACECAD                                *
 *                                                                     *
 ***********************************************************************/
TEST(AcecadTest, InputDeviceSectionWithOptionDevice)
{
    XOrgConfig config;
    XITServer server;

    config.AddInputSection("acecad", "--device--",
                           "Option \"CorePointer\" \"on\"\n"
                           "Option \"Device\" \"/dev/input/event0\"\n");
    config.AddDefaultScreenWithDriver();
    server.Start(config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
    int major = 2;
    int minor = 0;
    ASSERT_EQ(Success, XIQueryVersion(dpy, &major, &minor));

    int ndevices;
    XIDeviceInfo *info;

    info = XIQueryDevice(dpy, XIAllDevices, &ndevices);
    XIFreeDeviceInfo(info);

    /* VCP, VCK, xtest, mouse, keyboard, acecad */
    int expected_devices = 7;

    /* xserver git 1357cd7251 , no <default pointer> */
    if (server.GetVersion().compare("1.11.0") > 0)
	    expected_devices--;

    ASSERT_EQ(count_devices(dpy), expected_devices);
    ASSERT_EQ(FindInputDeviceByName(dpy, "--device--"), 1);

    config.RemoveConfig();
    server.RemoveLogFile();
}

/***********************************************************************
 *                                                                     *
 *                               AIPTEK                                *
 *                                                                     *
 ***********************************************************************/

class AiptekTest : public XITServerInputTest,
                   public ::testing::WithParamInterface<std::string> {
    virtual void SetUpConfigAndLog() {

        std::string type = GetParam();

        config.AddInputSection("aiptek", "--device--",
                "Option \"CorePointer\" \"on\"\n"
                "Option \"Device\" \"/dev/input/event0\"\n"
                "Option \"Type\" \"" + type + "\"");
        config.AddDefaultScreenWithDriver();
        config.WriteConfig();
    }
};

TEST_P(AiptekTest, InputDeviceSectionWithType)
{
    ::Display *dpy = Display();

    /* VCP, VCK, xtest, mouse, keyboard, aiptek */
    int expected_devices = 7;

    /* xserver git 1357cd7251, no <default pointer> */
    if (server.GetVersion().compare("1.11.0") > 0)
	    expected_devices--;

    ASSERT_EQ(count_devices(dpy), expected_devices);
    ASSERT_EQ(FindInputDeviceByName(dpy, "--device--"), 1);

    config.RemoveConfig();
    server.RemoveLogFile();
}

INSTANTIATE_TEST_CASE_P(, AiptekTest,
                        ::testing::Values("stylus", "cursor", "eraser"));

/***********************************************************************
 *                                                                     *
 *                            ELOGRAPHICS                              *
 *                                                                     *
 ***********************************************************************/
TEST(ElographicsTest, InputDeviceSectionWithOptionDevice)
{
    XOrgConfig config;
    XITServer server;

    config.AddInputSection("elographics", "--device--",
                           "Option \"CorePointer\" \"on\"\n"
                           "Option \"Device\" \"/dev/input/event0\"\n");
    config.AddDefaultScreenWithDriver();
    server.Start(config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
    int major = 2;
    int minor = 0;
    ASSERT_EQ(Success, XIQueryVersion(dpy, &major, &minor));

    /* VCP, VCK, xtest, mouse, keyboard, aiptek */
    int expected_devices = 7;

    /* xserver git 1357cd7251, no <default pointer> */
    if (server.GetVersion().compare("1.11.0") > 0)
	    expected_devices--;

    ASSERT_EQ(count_devices(dpy), expected_devices);

    int deviceid;
    if (FindInputDeviceByName(dpy, "--device--", &deviceid) != 1) {
        SCOPED_TRACE("\n"
                     "	Elographics device '--device--' not found.\n"
                     "	Maybe this is elographics < 1.4.\n"
                     "	Checking for TOUCHSCREEN instead, see git commit\n"
                     "	xf86-input-elographics-1.3.0-1-g55f337f");
        ASSERT_EQ(FindInputDeviceByName(dpy, "TOUCHSCREEN", &deviceid), 1);
    } else
        ASSERT_EQ(FindInputDeviceByName(dpy, "--device--", &deviceid), 1);

    int ndevices;
    XIDeviceInfo *info = XIQueryDevice(dpy, deviceid, &ndevices);
    ASSERT_EQ(ndevices, 1);

    ASSERT_EQ(info->use, XISlavePointer) << "https://bugs.freedesktop.org/show_bug.cgi?id=40870";
    ASSERT_EQ(info->attachment, VIRTUAL_CORE_POINTER_ID);

    XIFreeDeviceInfo(info);

    config.RemoveConfig();
    server.RemoveLogFile();
}

/* Create a named pipe that pumps out elographics 10-byte packets */
struct elo_packet {
    unsigned char header;               /* always 'U' */
    unsigned char expected;             /* always 'T' (touch packet) in this program */
    unsigned char has_pressure;         /* 0x80 if pressure is present, 0 otherwise */
    unsigned char x_lo;
    unsigned char x_hi;
    unsigned char y_lo;
    unsigned char y_hi;
    unsigned char pressure_hi;
    unsigned char pressure_lo;
    unsigned char checksum;             /* 0x88 + bytes[1..8] */
};

static void elo_init_packet(struct elo_packet *packet, short x, short y, short pressure)
{
    int i;

    packet->header = 'U';
    packet->expected = 'T';
    packet->has_pressure = 0x80;
    packet->x_lo = x & 0xFF;
    packet->x_hi = (x & 0xFF00) >> 8;
    packet->y_lo = y & 0xFF;
    packet->y_hi = (y & 0xFF00) >> 8;
    packet->pressure_lo = pressure & 0xFF;
    packet->pressure_hi = (pressure & 0xFF00) >> 8;

    packet->checksum = 0xaa;

    for (i = 0; i < 9; i++)
        packet->checksum += *(((unsigned char*)packet) + i);
}

static void elo_sighandler(int sig)
{
    unlink("/tmp/elographics.pipe");
    exit(0);
}

static void elo_sighandler_pipe(int sig)
{
}

int elo_simulate(void)
{
    int i;
    int rc, fd;
    struct elo_packet packet;
    const char *path = "/tmp/elographics.pipe";

    rc = mkfifo(path, 0x755);
    if (rc == -1) {
        perror("Failed to create pipe:");
        return 1;
    }

    signal(SIGINT, elo_sighandler);
    signal(SIGTERM, elo_sighandler);
    signal(SIGPIPE, elo_sighandler_pipe);

    fd = open(path, O_WRONLY);
    if (fd == -1) {
        perror("Failed to open pipe:");
        rc = 1;
        goto out;
    }

    for (i = 0; i < 30; i++) {
        elo_init_packet(&packet, 100 * i, 10, 10);
        rc = write(fd, &packet, sizeof(packet));
        if (rc == -1 && errno == SIGPIPE)
            i--;
        usleep(50000);
    }

    if (i == 30)
        rc = 0;

out:
    unlink(path);

    return rc;
}

pid_t elo_fork(void)
{
    /* fork here */
    pid_t pid = fork();
    if (pid == 0) { /* child */
#ifdef __linux
        prctl(PR_SET_PDEATHSIG, SIGTERM);
#endif
        if (elo_simulate())
            throw new std::runtime_error("simulation failed\n");
        exit(0);
    }
    return pid;
}

TEST(ElographicsTest, StylusMovement)
{
    XOrgConfig config;
    XITServer server;

    config.AddInputSection("elographics", "--device--",
                           "Option \"CorePointer\" \"on\"\n"
                           "Option \"DebugLevel\" \"10\"\n"
                           "Option \"Model\" \"Sunit dSeries\"" /* does not send packets to device*/
                           "Option \"Device\" \"/tmp/elographics.pipe\"\n");
    config.AddDefaultScreenWithDriver();

    /* fork here */
    elo_fork();

    /* parent */
    server.Start(config);

    ::Display *dpy = XOpenDisplay(server.GetDisplayString().c_str());
    int major = 2;
    int minor = 0;
    ASSERT_EQ(Success, XIQueryVersion(dpy, &major, &minor));

    XSelectInput(dpy, DefaultRootWindow(dpy), PointerMotionMask);
    XSync(dpy, False);

    if (FindInputDeviceByName(dpy, "--device--") != 1) {
        SCOPED_TRACE("\n"
                     "	Elographics device '--device--' not found.\n"
                     "	Maybe this is elographics < 1.4.\n"
                     "	Checking for TOUCHSCREEN instead, see git commit\n"
                     "	xf86-input-elographics-1.3.0-1-g55f337f");
        ASSERT_EQ(FindInputDeviceByName(dpy, "TOUCHSCREEN"), 1);
    } else
        ASSERT_EQ(FindInputDeviceByName(dpy, "--device--"), 1);

    ASSERT_EVENT(XEvent, first, dpy, MotionNotify);
    ASSERT_EVENT(XEvent, second, dpy, MotionNotify);
    ASSERT_LT(first->xmotion.x_root, second->xmotion.x_root);

    int status;
    wait(&status);

    config.RemoveConfig();
    server.RemoveLogFile();
}

int main(int argc, char **argv) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}
<|endoftext|>