Home | History | Annotate | Download | only in glue
      1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Tests for CppBoundClass, in conjunction with CppBindingExample.  Binds
      6 // a CppBindingExample class into JavaScript in a custom test shell and tests
      7 // the binding from the outside by loading JS into the shell.
      8 
      9 #include <vector>
     10 
     11 #include "base/message_loop.h"
     12 #include "base/string_util.h"
     13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebData.h"
     14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
     15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h"
     16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
     17 #include "webkit/glue/cpp_binding_example.h"
     18 #include "webkit/glue/webkit_glue.h"
     19 #include "webkit/tools/test_shell/test_shell_test.h"
     20 
     21 using WebKit::WebFrame;
     22 
     23 namespace {
     24 
     25 class CppBindingExampleSubObject : public CppBindingExample {
     26  public:
     27   CppBindingExampleSubObject() {
     28     sub_value_.Set("sub!");
     29     BindProperty("sub_value", &sub_value_);
     30   }
     31  private:
     32   CppVariant sub_value_;
     33 };
     34 
     35 
     36 class CppBindingExampleWithOptionalFallback : public CppBindingExample {
     37  public:
     38   CppBindingExampleWithOptionalFallback() {
     39     BindProperty("sub_object", sub_object_.GetAsCppVariant());
     40   }
     41 
     42   void set_fallback_method_enabled(bool state) {
     43     BindFallbackMethod(state ?
     44         &CppBindingExampleWithOptionalFallback::fallbackMethod
     45         : NULL);
     46   }
     47 
     48   // The fallback method does nothing, but because of it the JavaScript keeps
     49   // running when a nonexistent method is called on an object.
     50   void fallbackMethod(const CppArgumentList& args, CppVariant* result) {
     51   }
     52 
     53  private:
     54   CppBindingExampleSubObject sub_object_;
     55 };
     56 
     57 class ExampleTestShell : public TestShell {
     58  public:
     59 
     60   ExampleTestShell(bool use_fallback_method) {
     61     example_bound_class_.set_fallback_method_enabled(use_fallback_method);
     62   }
     63 
     64   // When called by WebViewDelegate::WindowObjectCleared method, this binds a
     65   // CppExampleObject to window.example.
     66   virtual void BindJSObjectsToWindow(WebFrame* frame) {
     67     example_bound_class_.BindToJavascript(frame, "example");
     68     // We use the layoutTestController binding for notifyDone.
     69     TestShell::BindJSObjectsToWindow(frame);
     70   }
     71 
     72   // This is a public interface to TestShell's protected method, so it
     73   // can be called by our CreateEmptyWindow.
     74   bool PublicInitialize(const std::string& starting_url) {
     75     return Initialize(GURL(starting_url));
     76   }
     77 
     78   CppBindingExampleWithOptionalFallback example_bound_class_;
     79 };
     80 
     81 class CppBoundClassTest : public TestShellTest {
     82  protected:
     83    // Adapted from TestShell::CreateNewWindow, this creates an
     84    // ExampleTestShellWindow rather than a regular TestShell.
     85    virtual void CreateEmptyWindow() {
     86      ExampleTestShell* host = new ExampleTestShell(useFallback());
     87      ASSERT_TRUE(host != NULL);
     88      bool rv = host->PublicInitialize("about:blank");
     89      if (rv) {
     90        test_shell_ = host;
     91        TestShell::windowList()->push_back(host->mainWnd());
     92        webframe_ = test_shell_->webView()->mainFrame();
     93        ASSERT_TRUE(webframe_ != NULL);
     94      } else {
     95        delete host;
     96      }
     97    }
     98 
     99    // Wraps the given JavaScript snippet in <html><body><script> tags, then
    100    // loads it into a webframe so it is executed.
    101    void ExecuteJavaScript(const std::string& javascript) {
    102      std::string html = "<html><body>";
    103      html.append(TestShellTest::kJavascriptDelayExitScript);
    104      html.append("<script>");
    105      html.append(javascript);
    106      html.append("</script></body></html>");
    107      // The base URL doesn't matter.
    108      webframe_->loadHTMLString(html, GURL("about:blank"));
    109 
    110      test_shell_->WaitTestFinished();
    111     }
    112 
    113    // Executes the specified JavaScript and checks to be sure that the resulting
    114    // document text is exactly "SUCCESS".
    115    void CheckJavaScriptSuccess(const std::string& javascript) {
    116      ExecuteJavaScript(javascript);
    117      EXPECT_EQ("SUCCESS",
    118                UTF16ToASCII(webkit_glue::DumpDocumentText(webframe_)));
    119    }
    120 
    121    // Executes the specified JavaScript and checks that the resulting document
    122    // text is empty.
    123    void CheckJavaScriptFailure(const std::string& javascript) {
    124      ExecuteJavaScript(javascript);
    125      EXPECT_EQ("", UTF16ToASCII(webkit_glue::DumpDocumentText(webframe_)));
    126    }
    127 
    128    // Constructs a JavaScript snippet that evaluates and compares the left and
    129    // right expressions, printing "SUCCESS" to the page if they are equal and
    130    // printing their actual values if they are not.  Any strings in the
    131    // expressions should be enclosed in single quotes, and no double quotes
    132    // should appear in either expression (even if escaped). (If a test case
    133    // is added that needs fancier quoting, Json::valueToQuotedString could be
    134    // used here.  For now, it's not worth adding the dependency.)
    135    std::string BuildJSCondition(std::string left, std::string right) {
    136      return "var leftval = " + left + ";" +
    137             "var rightval = " + right + ";" +
    138             "if (leftval == rightval) {" +
    139             "  document.writeln('SUCCESS');" +
    140             "} else {" +
    141             "  document.writeln(\"" +
    142                  left + " [\" + leftval + \"] != " +
    143                  right + " [\" + rightval + \"]\");" +
    144             "}";
    145    }
    146 
    147 protected:
    148   virtual bool useFallback() {
    149     return false;
    150   }
    151 
    152 private:
    153   WebFrame* webframe_;
    154 };
    155 
    156 class CppBoundClassWithFallbackMethodTest : public CppBoundClassTest {
    157 protected:
    158   virtual bool useFallback() {
    159     return true;
    160   }
    161 };
    162 
    163 // Ensures that the example object has been bound to JS.
    164 TEST_F(CppBoundClassTest, ObjectExists) {
    165   std::string js = BuildJSCondition("typeof window.example", "'object'");
    166   CheckJavaScriptSuccess(js);
    167 
    168   // An additional check to test our test.
    169   js = BuildJSCondition("typeof window.invalid_object", "'undefined'");
    170   CheckJavaScriptSuccess(js);
    171 }
    172 
    173 TEST_F(CppBoundClassTest, PropertiesAreInitialized) {
    174   std::string js = BuildJSCondition("example.my_value", "10");
    175   CheckJavaScriptSuccess(js);
    176 
    177   js = BuildJSCondition("example.my_other_value", "'Reinitialized!'");
    178   CheckJavaScriptSuccess(js);
    179 }
    180 
    181 TEST_F(CppBoundClassTest, SubOject) {
    182   std::string js = BuildJSCondition("typeof window.example.sub_object",
    183                                     "'object'");
    184   CheckJavaScriptSuccess(js);
    185 
    186   js = BuildJSCondition("example.sub_object.sub_value", "'sub!'");
    187   CheckJavaScriptSuccess(js);
    188 }
    189 
    190 TEST_F(CppBoundClassTest, SetAndGetProperties) {
    191   // The property on the left will be set to the value on the right, then
    192   // checked to make sure it holds that same value.
    193   static const std::string tests[] = {
    194     "example.my_value", "7",
    195     "example.my_value", "'test'",
    196     "example.my_other_value", "3.14",
    197     "example.my_other_value", "false",
    198     "" // Array end marker: insert additional test pairs before this.
    199   };
    200 
    201   for (int i = 0; tests[i] != ""; i += 2) {
    202     std::string left = tests[i];
    203     std::string right = tests[i + 1];
    204     // left = right;
    205     std::string js = left;
    206     js.append(" = ");
    207     js.append(right);
    208     js.append(";");
    209     js.append(BuildJSCondition(left, right));
    210     CheckJavaScriptSuccess(js);
    211   }
    212 }
    213 
    214 TEST_F(CppBoundClassTest, SetAndGetPropertiesWithCallbacks) {
    215   // TODO(dglazkov): fix NPObject issues around failing property setters and
    216   // getters and add tests for situations when GetProperty or SetProperty fail.
    217   std::string js = "var result = 'SUCCESS';\n"
    218     "example.my_value_with_callback = 10;\n"
    219     "if (example.my_value_with_callback != 10)\n"
    220     "  result = 'FAIL: unable to set property.';\n"
    221     "example.my_value_with_callback = 11;\n"
    222     "if (example.my_value_with_callback != 11)\n"
    223     "  result = 'FAIL: unable to set property again';\n"
    224     "if (example.same != 42)\n"
    225     "  result = 'FAIL: same property should always be 42';\n"
    226     "example.same = 24;\n"
    227     "if (example.same != 42)\n"
    228     "  result = 'FAIL: same property should always be 42';\n"
    229     "document.writeln(result);\n";
    230   CheckJavaScriptSuccess(js);
    231 }
    232 
    233 TEST_F(CppBoundClassTest, InvokeMethods) {
    234   // The expression on the left is expected to return the value on the right.
    235   static const std::string tests[] = {
    236     "example.echoValue(true)", "true",
    237     "example.echoValue(13)", "13",
    238     "example.echoValue(2.718)", "2.718",
    239     "example.echoValue('yes')", "'yes'",
    240     "example.echoValue()", "null",     // Too few arguments
    241 
    242     "example.echoType(false)", "true",
    243     "example.echoType(19)", "3.14159",
    244     "example.echoType(9.876)", "3.14159",
    245     "example.echoType('test string')", "'Success!'",
    246     "example.echoType()", "null",      // Too few arguments
    247 
    248     // Comparing floats that aren't integer-valued is usually problematic due
    249     // to rounding, but exact powers of 2 should also be safe.
    250     "example.plus(2.5, 18.0)", "20.5",
    251     "example.plus(2, 3.25)", "5.25",
    252     "example.plus(2, 3)", "5",
    253     "example.plus()", "null",             // Too few arguments
    254     "example.plus(1)", "null",            // Too few arguments
    255     "example.plus(1, 'test')", "null",    // Wrong argument type
    256     "example.plus('test', 2)", "null",    // Wrong argument type
    257     "example.plus('one', 'two')", "null", // Wrong argument type
    258     "" // Array end marker: insert additional test pairs before this.
    259   };
    260 
    261   for (int i = 0; tests[i] != ""; i+= 2) {
    262     std::string left = tests[i];
    263     std::string right = tests[i + 1];
    264     std::string js = BuildJSCondition(left, right);
    265     CheckJavaScriptSuccess(js);
    266   }
    267 
    268   std::string js = "example.my_value = 3.25; example.my_other_value = 1.25;";
    269   js.append(BuildJSCondition(
    270       "example.plus(example.my_value, example.my_other_value)", "4.5"));
    271   CheckJavaScriptSuccess(js);
    272 }
    273 
    274 // Tests that invoking a nonexistent method with no fallback method stops the
    275 // script's execution
    276 TEST_F(CppBoundClassTest,
    277        InvokeNonexistentMethodNoFallback) {
    278   std::string js = "example.nonExistentMethod();document.writeln('SUCCESS');";
    279   CheckJavaScriptFailure(js);
    280 }
    281 
    282 // Ensures existent methods can be invoked successfully when the fallback method
    283 // is used
    284 TEST_F(CppBoundClassWithFallbackMethodTest,
    285        InvokeExistentMethodsWithFallback) {
    286   std::string js = BuildJSCondition("example.echoValue(34)", "34");
    287   CheckJavaScriptSuccess(js);
    288 }
    289 
    290 }  // namespace
    291