普通文本  |  242行  |  7.55 KB

// Copyright (c) 2012 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.

// Tests for CppBoundClass, in conjunction with CppBindingExample.  Binds
// a CppBindingExample class into JavaScript in a custom test shell and tests
// the binding from the outside by loading JS into the shell.

#include "base/strings/utf_string_conversions.h"
#include "content/public/common/url_constants.h"
#include "content/public/renderer/render_view_observer.h"
#include "content/public/test/render_view_test.h"
#include "content/test/cpp_binding_example.h"
#include "third_party/WebKit/public/platform/WebURLRequest.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebElement.h"

using webkit_glue::CppArgumentList;
using webkit_glue::CppVariant;

namespace content {

namespace {

class CppBindingExampleSubObject : public CppBindingExample {
 public:
  CppBindingExampleSubObject() {
    sub_value_.Set("sub!");
    BindProperty("sub_value", &sub_value_);
  }
 private:
  CppVariant sub_value_;
};


class CppBindingExampleWithOptionalFallback : public CppBindingExample {
 public:
  CppBindingExampleWithOptionalFallback() {
    BindProperty("sub_object", sub_object_.GetAsCppVariant());
  }

  void set_fallback_method_enabled(bool state) {
    BindFallbackCallback(state ?
        base::Bind(&CppBindingExampleWithOptionalFallback::fallbackMethod,
                   base::Unretained(this))
        : CppBoundClass::Callback());
  }

  // The fallback method does nothing, but because of it the JavaScript keeps
  // running when a nonexistent method is called on an object.
  void fallbackMethod(const CppArgumentList& args, CppVariant* result) {
  }

 private:
  CppBindingExampleSubObject sub_object_;
};

class TestObserver : public RenderViewObserver {
 public:
  explicit TestObserver(RenderView* render_view)
      : RenderViewObserver(render_view) {}
  virtual void DidClearWindowObject(blink::WebFrame* frame) OVERRIDE {
    example_bound_class_.BindToJavascript(frame, "example");
  }
  void set_fallback_method_enabled(bool use_fallback) {
    example_bound_class_.set_fallback_method_enabled(use_fallback);
  }
 private:
  CppBindingExampleWithOptionalFallback example_bound_class_;
};

}  // namespace

class CppBoundClassTest : public RenderViewTest {
 public:
  CppBoundClassTest() {}

  virtual void SetUp() OVERRIDE {
    RenderViewTest::SetUp();
    observer_.reset(new TestObserver(view_));
    observer_->set_fallback_method_enabled(useFallback());

    blink::WebURLRequest url_request;
    url_request.initialize();
    url_request.setURL(GURL(kAboutBlankURL));

    GetMainFrame()->loadRequest(url_request);
    ProcessPendingMessages();
  }

  virtual void TearDown() OVERRIDE {
    observer_.reset();
    RenderViewTest::TearDown();
  }

  // Executes the specified JavaScript and checks that the resulting document
  // text is empty.
  void CheckJavaScriptFailure(const std::string& javascript) {
    ExecuteJavaScript(javascript.c_str());
    EXPECT_EQ(
        "",
        UTF16ToASCII(GetMainFrame()->document().documentElement().innerText()));
  }

  void CheckTrue(const std::string& expression) {
    int was_page_a = -1;
    base::string16 check_page_a =
        ASCIIToUTF16(std::string("Number(") + expression + ")");
    EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
    EXPECT_EQ(1, was_page_a);
  }

 protected:
  virtual bool useFallback() {
    return false;
  }

 private:
  scoped_ptr<TestObserver> observer_;
};

class CppBoundClassWithFallbackMethodTest : public CppBoundClassTest {
 protected:
  virtual bool useFallback() OVERRIDE {
    return true;
  }
};

// Ensures that the example object has been bound to JS.
TEST_F(CppBoundClassTest, ObjectExists) {
  CheckTrue("typeof window.example == 'object'");

  // An additional check to test our test.
  CheckTrue("typeof window.invalid_object == 'undefined'");
}

TEST_F(CppBoundClassTest, PropertiesAreInitialized) {
  CheckTrue("example.my_value == 10");

  CheckTrue("example.my_other_value == 'Reinitialized!'");
}

TEST_F(CppBoundClassTest, SubOject) {
  CheckTrue("typeof window.example.sub_object == 'object'");

  CheckTrue("example.sub_object.sub_value == 'sub!'");
}

TEST_F(CppBoundClassTest, SetAndGetProperties) {
  // The property on the left will be set to the value on the right, then
  // checked to make sure it holds that same value.
  static const std::string tests[] = {
    "example.my_value", "7",
    "example.my_value", "'test'",
    "example.my_other_value", "3.14",
    "example.my_other_value", "false",
    "" // Array end marker: insert additional test pairs before this.
  };

  for (int i = 0; tests[i] != ""; i += 2) {
    std::string left = tests[i];
    std::string right = tests[i + 1];
    // left = right;
    std::string js = left;
    js.append(" = ");
    js.append(right);
    js.append(";");
    ExecuteJavaScript(js.c_str());
    std::string expression = left;
    expression += " == ";
    expression += right;
    CheckTrue(expression);
  }
}

TEST_F(CppBoundClassTest, SetAndGetPropertiesWithCallbacks) {
  // TODO(dglazkov): fix NPObject issues around failing property setters and
  // getters and add tests for situations when GetProperty or SetProperty fail.
  ExecuteJavaScript("example.my_value_with_callback = 10;");
  CheckTrue("example.my_value_with_callback == 10");

  ExecuteJavaScript("example.my_value_with_callback = 11;");
  CheckTrue("example.my_value_with_callback == 11");

  CheckTrue("example.same == 42");

  ExecuteJavaScript("example.same = 24;");
  CheckTrue("example.same == 42");
}

TEST_F(CppBoundClassTest, InvokeMethods) {
  // The expression on the left is expected to return the value on the right.
  static const std::string tests[] = {
    "example.echoValue(true) == true",
    "example.echoValue(13) == 13",
    "example.echoValue(2.718) == 2.718",
    "example.echoValue('yes') == 'yes'",
    "example.echoValue() == null",     // Too few arguments

    "example.echoType(false) == true",
    "example.echoType(19) == 3.14159",
    "example.echoType(9.876) == 3.14159",
    "example.echoType('test string') == 'Success!'",
    "example.echoType() == null",      // Too few arguments

    // Comparing floats that aren't integer-valued is usually problematic due
    // to rounding, but exact powers of 2 should also be safe.
    "example.plus(2.5, 18.0) == 20.5",
    "example.plus(2, 3.25) == 5.25",
    "example.plus(2, 3) == 5",
    "example.plus() == null",             // Too few arguments
    "example.plus(1) == null",            // Too few arguments
    "example.plus(1, 'test') == null",    // Wrong argument type
    "example.plus('test', 2) == null",    // Wrong argument type
    "example.plus('one', 'two') == null", // Wrong argument type
    "" // Array end marker: insert additional test pairs before this.
  };

  for (int i = 0; tests[i] != ""; i++)
    CheckTrue(tests[i]);

  ExecuteJavaScript("example.my_value = 3.25; example.my_other_value = 1.25;");
  CheckTrue("example.plus(example.my_value, example.my_other_value) == 4.5");
}

// Tests that invoking a nonexistent method with no fallback method stops the
// script's execution
TEST_F(CppBoundClassTest,
       InvokeNonexistentMethodNoFallback) {
  std::string js = "example.nonExistentMethod();document.writeln('SUCCESS');";
  CheckJavaScriptFailure(js);
}

// Ensures existent methods can be invoked successfully when the fallback method
// is used
TEST_F(CppBoundClassWithFallbackMethodTest,
       InvokeExistentMethodsWithFallback) {
  CheckTrue("example.echoValue(34) == 34");
}

}  // namespace content