Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1659)

Side by Side Diff: extensions/renderer/argument_spec_unittest.cc

Issue 2947463002: [Extensions Bindings] Add a bindings/ subdirectory under renderer (Closed)
Patch Set: . Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 #include "extensions/renderer/argument_spec.h"
6 #include "base/memory/ptr_util.h"
7 #include "base/values.h"
8 #include "extensions/renderer/api_binding_test_util.h"
9 #include "extensions/renderer/api_invocation_errors.h"
10 #include "extensions/renderer/api_type_reference_map.h"
11 #include "gin/converter.h"
12 #include "gin/public/isolate_holder.h"
13 #include "gin/test/v8_test.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "v8/include/v8.h"
16
17 namespace extensions {
18
19 class ArgumentSpecUnitTest : public gin::V8Test {
20 protected:
21 ArgumentSpecUnitTest()
22 : type_refs_(APITypeReferenceMap::InitializeTypeCallback()) {}
23 ~ArgumentSpecUnitTest() override {}
24
25 enum class TestResult {
26 PASS,
27 FAIL,
28 THROW,
29 };
30
31 struct RunTestParams {
32 RunTestParams(const ArgumentSpec& spec,
33 base::StringPiece script_source,
34 TestResult result)
35 : spec(spec), script_source(script_source), expected_result(result) {}
36
37 const ArgumentSpec& spec;
38 base::StringPiece script_source;
39 TestResult expected_result;
40 base::StringPiece expected_json;
41 base::StringPiece expected_error;
42 base::StringPiece expected_thrown_message;
43 const base::Value* expected_value = nullptr;
44 bool should_convert = true;
45 };
46
47 void ExpectSuccess(const ArgumentSpec& spec,
48 const std::string& script_source,
49 const std::string& expected_json_single_quotes) {
50 RunTestParams params(spec, script_source, TestResult::PASS);
51 std::string expected_json =
52 ReplaceSingleQuotes(expected_json_single_quotes);
53 params.expected_json = expected_json;
54 RunTest(params);
55 }
56
57 void ExpectSuccess(const ArgumentSpec& spec,
58 const std::string& script_source,
59 const base::Value& expected_value) {
60 RunTestParams params(spec, script_source, TestResult::PASS);
61 params.expected_value = &expected_value;
62 RunTest(params);
63 }
64
65 void ExpectSuccessWithNoConversion(const ArgumentSpec& spec,
66 const std::string& script_source) {
67 RunTestParams params(spec, script_source, TestResult::PASS);
68 params.should_convert = false;
69 RunTest(params);
70 }
71
72 void ExpectFailure(const ArgumentSpec& spec,
73 const std::string& script_source,
74 const std::string& expected_error) {
75 RunTestParams params(spec, script_source, TestResult::FAIL);
76 params.expected_error = expected_error;
77 RunTest(params);
78 }
79
80 void ExpectFailureWithNoConversion(const ArgumentSpec& spec,
81 const std::string& script_source,
82 const std::string& expected_error) {
83 RunTestParams params(spec, script_source, TestResult::FAIL);
84 params.should_convert = false;
85 params.expected_error = expected_error;
86 RunTest(params);
87 }
88
89 void ExpectThrow(const ArgumentSpec& spec,
90 const std::string& script_source,
91 const std::string& expected_thrown_message) {
92 RunTestParams params(spec, script_source, TestResult::THROW);
93 params.expected_thrown_message = expected_thrown_message;
94 RunTest(params);
95 }
96
97 void AddTypeRef(const std::string& id, std::unique_ptr<ArgumentSpec> spec) {
98 type_refs_.AddSpec(id, std::move(spec));
99 }
100
101 private:
102 void RunTest(const RunTestParams& params);
103
104 APITypeReferenceMap type_refs_;
105
106 DISALLOW_COPY_AND_ASSIGN(ArgumentSpecUnitTest);
107 };
108
109 void ArgumentSpecUnitTest::RunTest(const RunTestParams& params) {
110 v8::Isolate* isolate = instance_->isolate();
111 v8::HandleScope handle_scope(instance_->isolate());
112
113 v8::Local<v8::Context> context =
114 v8::Local<v8::Context>::New(instance_->isolate(), context_);
115 v8::TryCatch try_catch(isolate);
116 v8::Local<v8::Value> val =
117 V8ValueFromScriptSource(context, params.script_source);
118 ASSERT_FALSE(val.IsEmpty()) << params.script_source;
119
120 std::string error;
121 std::unique_ptr<base::Value> out_value;
122 bool did_succeed = params.spec.ParseArgument(
123 context, val, type_refs_, params.should_convert ? &out_value : nullptr,
124 &error);
125 bool should_succeed = params.expected_result == TestResult::PASS;
126 ASSERT_EQ(should_succeed, did_succeed)
127 << params.script_source << ", " << error;
128 ASSERT_EQ(did_succeed && params.should_convert, !!out_value);
129 bool should_throw = params.expected_result == TestResult::THROW;
130 ASSERT_EQ(should_throw, try_catch.HasCaught()) << params.script_source;
131
132 if (!params.expected_error.empty())
133 EXPECT_EQ(params.expected_error, error) << params.script_source;
134
135 if (should_succeed && params.should_convert) {
136 ASSERT_TRUE(out_value);
137 if (params.expected_value)
138 EXPECT_TRUE(params.expected_value->Equals(out_value.get()))
139 << params.script_source;
140 else
141 EXPECT_EQ(params.expected_json, ValueToString(*out_value));
142 } else if (should_throw) {
143 EXPECT_EQ(params.expected_thrown_message,
144 gin::V8ToString(try_catch.Message()->Get()));
145 }
146 }
147
148 TEST_F(ArgumentSpecUnitTest, Test) {
149 using namespace api_errors;
150
151 {
152 ArgumentSpec spec(*ValueFromString("{'type': 'integer'}"));
153 ExpectSuccess(spec, "1", "1");
154 ExpectSuccess(spec, "-1", "-1");
155 ExpectSuccess(spec, "0", "0");
156 ExpectSuccess(spec, "0.0", "0");
157 ExpectFailure(spec, "undefined", InvalidType(kTypeInteger, kTypeUndefined));
158 ExpectFailure(spec, "null", InvalidType(kTypeInteger, kTypeNull));
159 ExpectFailure(spec, "1.1", InvalidType(kTypeInteger, kTypeDouble));
160 ExpectFailure(spec, "'foo'", InvalidType(kTypeInteger, kTypeString));
161 ExpectFailure(spec, "'1'", InvalidType(kTypeInteger, kTypeString));
162 ExpectFailure(spec, "({})", InvalidType(kTypeInteger, kTypeObject));
163 ExpectFailure(spec, "[1]", InvalidType(kTypeInteger, kTypeList));
164 }
165
166 {
167 ArgumentSpec spec(*ValueFromString("{'type': 'number'}"));
168 ExpectSuccess(spec, "1", "1.0");
169 ExpectSuccess(spec, "-1", "-1.0");
170 ExpectSuccess(spec, "0", "0.0");
171 ExpectSuccess(spec, "1.1", "1.1");
172 ExpectSuccess(spec, "1.", "1.0");
173 ExpectSuccess(spec, ".1", "0.1");
174 ExpectFailure(spec, "undefined", InvalidType(kTypeDouble, kTypeUndefined));
175 ExpectFailure(spec, "null", InvalidType(kTypeDouble, kTypeNull));
176 ExpectFailure(spec, "'foo'", InvalidType(kTypeDouble, kTypeString));
177 ExpectFailure(spec, "'1.1'", InvalidType(kTypeDouble, kTypeString));
178 ExpectFailure(spec, "({})", InvalidType(kTypeDouble, kTypeObject));
179 ExpectFailure(spec, "[1.1]", InvalidType(kTypeDouble, kTypeList));
180 }
181
182 {
183 ArgumentSpec spec(*ValueFromString("{'type': 'integer', 'minimum': 1}"));
184 ExpectSuccess(spec, "2", "2");
185 ExpectSuccess(spec, "1", "1");
186 ExpectFailure(spec, "0", NumberTooSmall(1));
187 ExpectFailure(spec, "-1", NumberTooSmall(1));
188 }
189
190 {
191 ArgumentSpec spec(*ValueFromString("{'type': 'integer', 'maximum': 10}"));
192 ExpectSuccess(spec, "10", "10");
193 ExpectSuccess(spec, "1", "1");
194 ExpectFailure(spec, "11", NumberTooLarge(10));
195 }
196
197 {
198 ArgumentSpec spec(*ValueFromString("{'type': 'string'}"));
199 ExpectSuccess(spec, "'foo'", "'foo'");
200 ExpectSuccess(spec, "''", "''");
201 ExpectFailure(spec, "1", InvalidType(kTypeString, kTypeInteger));
202 ExpectFailure(spec, "({})", InvalidType(kTypeString, kTypeObject));
203 ExpectFailure(spec, "['foo']", InvalidType(kTypeString, kTypeList));
204 }
205
206 {
207 ArgumentSpec spec(
208 *ValueFromString("{'type': 'string', 'enum': ['foo', 'bar']}"));
209 std::set<std::string> valid_enums = {"foo", "bar"};
210 ExpectSuccess(spec, "'foo'", "'foo'");
211 ExpectSuccess(spec, "'bar'", "'bar'");
212 ExpectFailure(spec, "['foo']", InvalidType(kTypeString, kTypeList));
213 ExpectFailure(spec, "'fo'", InvalidEnumValue(valid_enums));
214 ExpectFailure(spec, "'foobar'", InvalidEnumValue(valid_enums));
215 ExpectFailure(spec, "'baz'", InvalidEnumValue(valid_enums));
216 ExpectFailure(spec, "''", InvalidEnumValue(valid_enums));
217 }
218
219 {
220 ArgumentSpec spec(*ValueFromString(
221 "{'type': 'string', 'enum': [{'name': 'foo'}, {'name': 'bar'}]}"));
222 std::set<std::string> valid_enums = {"foo", "bar"};
223 ExpectSuccess(spec, "'foo'", "'foo'");
224 ExpectSuccess(spec, "'bar'", "'bar'");
225 ExpectFailure(spec, "['foo']", InvalidType(kTypeString, kTypeList));
226 ExpectFailure(spec, "'fo'", InvalidEnumValue(valid_enums));
227 ExpectFailure(spec, "'foobar'", InvalidEnumValue(valid_enums));
228 ExpectFailure(spec, "'baz'", InvalidEnumValue(valid_enums));
229 ExpectFailure(spec, "''", InvalidEnumValue(valid_enums));
230 }
231
232 {
233 ArgumentSpec spec(*ValueFromString("{'type': 'boolean'}"));
234 ExpectSuccess(spec, "true", "true");
235 ExpectSuccess(spec, "false", "false");
236 ExpectFailure(spec, "1", InvalidType(kTypeBoolean, kTypeInteger));
237 ExpectFailure(spec, "'true'", InvalidType(kTypeBoolean, kTypeString));
238 ExpectFailure(spec, "null", InvalidType(kTypeBoolean, kTypeNull));
239 }
240
241 {
242 ArgumentSpec spec(
243 *ValueFromString("{'type': 'array', 'items': {'type': 'string'}}"));
244 ExpectSuccess(spec, "[]", "[]");
245 ExpectSuccess(spec, "['foo']", "['foo']");
246 ExpectSuccess(spec, "['foo', 'bar']", "['foo','bar']");
247 ExpectSuccess(spec, "var x = new Array(); x[0] = 'foo'; x;", "['foo']");
248 ExpectFailure(spec, "'foo'", InvalidType(kTypeList, kTypeString));
249 ExpectFailure(spec, "[1, 2]",
250 IndexError(0u, InvalidType(kTypeString, kTypeInteger)));
251 ExpectFailure(spec, "['foo', 1]",
252 IndexError(1u, InvalidType(kTypeString, kTypeInteger)));
253 ExpectFailure(spec,
254 "var x = ['a', 'b', 'c'];"
255 "x[4] = 'd';" // x[3] is undefined, violating the spec.
256 "x;",
257 IndexError(3u, InvalidType(kTypeString, kTypeUndefined)));
258 ExpectThrow(
259 spec,
260 "var x = [];"
261 "Object.defineProperty("
262 " x, 0,"
263 " { get: () => { throw new Error('Badness'); } });"
264 "x;",
265 "Uncaught Error: Badness");
266 }
267
268 {
269 const char kObjectSpec[] =
270 "{"
271 " 'type': 'object',"
272 " 'properties': {"
273 " 'prop1': {'type': 'string'},"
274 " 'prop2': {'type': 'integer', 'optional': true}"
275 " }"
276 "}";
277 ArgumentSpec spec(*ValueFromString(kObjectSpec));
278 ExpectSuccess(spec, "({prop1: 'foo', prop2: 2})",
279 "{'prop1':'foo','prop2':2}");
280 ExpectSuccess(spec, "({prop1: 'foo'})", "{'prop1':'foo'}");
281 ExpectSuccess(spec, "({prop1: 'foo', prop2: null})", "{'prop1':'foo'}");
282 ExpectSuccess(spec, "x = {}; x.prop1 = 'foo'; x;", "{'prop1':'foo'}");
283 ExpectFailure(
284 spec, "({prop1: 'foo', prop2: 'bar'})",
285 PropertyError("prop2", InvalidType(kTypeInteger, kTypeString)));
286 ExpectFailure(spec, "({prop2: 2})", MissingRequiredProperty("prop1"));
287 // Unknown properties are not allowed.
288 ExpectFailure(spec, "({prop1: 'foo', prop2: 2, prop3: 'blah'})",
289 UnexpectedProperty("prop3"));
290 // We only consider properties on the object itself, not its prototype
291 // chain.
292 ExpectFailure(spec,
293 "function X() {}\n"
294 "X.prototype = { prop1: 'foo' };\n"
295 "var x = new X();\n"
296 "x;",
297 MissingRequiredProperty("prop1"));
298 ExpectFailure(spec,
299 "function X() {}\n"
300 "X.prototype = { prop1: 'foo' };\n"
301 "function Y() { this.__proto__ = X.prototype; }\n"
302 "var z = new Y();\n"
303 "z;",
304 MissingRequiredProperty("prop1"));
305 // Self-referential fun. Currently we don't have to worry about these much
306 // because the spec won't match at some point (and V8ValueConverter has
307 // cycle detection and will fail).
308 ExpectFailure(
309 spec, "x = {}; x.prop1 = x; x;",
310 PropertyError("prop1", InvalidType(kTypeString, kTypeObject)));
311 ExpectThrow(
312 spec,
313 "({ get prop1() { throw new Error('Badness'); }});",
314 "Uncaught Error: Badness");
315 ExpectThrow(spec,
316 "x = {prop1: 'foo'};\n"
317 "Object.defineProperty(\n"
318 " x, 'prop2',\n"
319 " {\n"
320 " get: () => { throw new Error('Badness'); },\n"
321 " enumerable: true,\n"
322 "});\n"
323 "x;",
324 "Uncaught Error: Badness");
325 // By default, properties from Object.defineProperty() aren't enumerable,
326 // so they will be ignored in our matching.
327 ExpectSuccess(spec,
328 "x = {prop1: 'foo'};\n"
329 "Object.defineProperty(\n"
330 " x, 'prop2',\n"
331 " { get: () => { throw new Error('Badness'); } });\n"
332 "x;",
333 "{'prop1':'foo'}");
334 }
335
336 {
337 const char kFunctionSpec[] = "{ 'type': 'function' }";
338 ArgumentSpec spec(*ValueFromString(kFunctionSpec));
339 // Functions are serialized as empty dictionaries.
340 ExpectSuccess(spec, "(function() {})", "{}");
341 ExpectSuccessWithNoConversion(spec, "(function() {})");
342 ExpectSuccessWithNoConversion(spec, "(function(a, b) { a(); b(); })");
343 ExpectSuccessWithNoConversion(spec, "(function(a, b) { a(); b(); })");
344 ExpectFailureWithNoConversion(spec, "({a: function() {}})",
345 InvalidType(kTypeFunction, kTypeObject));
346 ExpectFailureWithNoConversion(spec, "([function() {}])",
347 InvalidType(kTypeFunction, kTypeList));
348 ExpectFailureWithNoConversion(spec, "1",
349 InvalidType(kTypeFunction, kTypeInteger));
350 }
351
352 {
353 const char kBinarySpec[] = "{ 'type': 'binary' }";
354 ArgumentSpec spec(*ValueFromString(kBinarySpec));
355 // Simple case: empty ArrayBuffer -> empty BinaryValue.
356 ExpectSuccess(spec, "(new ArrayBuffer())",
357 base::Value(base::Value::Type::BINARY));
358 {
359 // A non-empty (but zero-filled) ArrayBufferView.
360 const char kBuffer[] = {0, 0, 0, 0};
361 std::unique_ptr<base::Value> expected_value =
362 base::Value::CreateWithCopiedBuffer(kBuffer, arraysize(kBuffer));
363 ASSERT_TRUE(expected_value);
364 ExpectSuccessWithNoConversion(spec, "(new Int32Array(2))");
365 }
366 {
367 // Actual data.
368 const char kBuffer[] = {'p', 'i', 'n', 'g'};
369 std::unique_ptr<base::Value> expected_value =
370 base::Value::CreateWithCopiedBuffer(kBuffer, arraysize(kBuffer));
371 ASSERT_TRUE(expected_value);
372 ExpectSuccess(spec,
373 "var b = new ArrayBuffer(4);\n"
374 "var v = new Uint8Array(b);\n"
375 "var s = 'ping';\n"
376 "for (var i = 0; i < s.length; ++i)\n"
377 " v[i] = s.charCodeAt(i);\n"
378 "b;",
379 *expected_value);
380 }
381 ExpectFailure(spec, "1", InvalidType(kTypeBinary, kTypeInteger));
382 }
383 {
384 const char kAnySpec[] = "{ 'type': 'any' }";
385 ArgumentSpec spec(*ValueFromString(kAnySpec));
386 ExpectSuccess(spec, "42", "42");
387 ExpectSuccess(spec, "'foo'", "'foo'");
388 ExpectSuccess(spec, "({prop1:'bar'})", "{'prop1':'bar'}");
389 ExpectSuccess(spec, "[1, 2, 3]", "[1,2,3]");
390 ExpectSuccess(spec, "[1, 'a']", "[1,'a']");
391 ExpectSuccess(spec, "null", base::Value());
392 ExpectSuccess(spec, "({prop1: 'alpha', prop2: null})", "{'prop1':'alpha'}");
393 ExpectSuccess(spec,
394 "x = {alpha: 'alpha'};\n"
395 "y = {beta: 'beta', x: x};\n"
396 "y;",
397 "{'beta':'beta','x':{'alpha':'alpha'}}");
398 // We don't serialize undefined.
399 // TODO(devlin): This matches current behavior, but should it? Part of the
400 // problem is that base::Values don't differentiate between undefined and
401 // null, which is a potentially important distinction. However, this means
402 // that in serialization of an object {a: 1, foo:undefined}, we lose the
403 // 'foo' property.
404 ExpectFailure(spec, "undefined", UnserializableValue());
405
406 ExpectSuccess(spec, "({prop1: 1, prop2: undefined})", "{'prop1':1}");
407 }
408 }
409
410 TEST_F(ArgumentSpecUnitTest, TypeRefsTest) {
411 using namespace api_errors;
412 const char kObjectType[] =
413 "{"
414 " 'id': 'refObj',"
415 " 'type': 'object',"
416 " 'properties': {"
417 " 'prop1': {'type': 'string'},"
418 " 'prop2': {'type': 'integer', 'optional': true}"
419 " }"
420 "}";
421 const char kEnumType[] =
422 "{'id': 'refEnum', 'type': 'string', 'enum': ['alpha', 'beta']}";
423 AddTypeRef("refObj",
424 base::MakeUnique<ArgumentSpec>(*ValueFromString(kObjectType)));
425 AddTypeRef("refEnum",
426 base::MakeUnique<ArgumentSpec>(*ValueFromString(kEnumType)));
427 std::set<std::string> valid_enums = {"alpha", "beta"};
428
429 {
430 const char kObjectWithRefEnumSpec[] =
431 "{"
432 " 'name': 'objWithRefEnum',"
433 " 'type': 'object',"
434 " 'properties': {"
435 " 'e': {'$ref': 'refEnum'},"
436 " 'sub': {'type': 'integer'}"
437 " }"
438 "}";
439 ArgumentSpec spec(*ValueFromString(kObjectWithRefEnumSpec));
440 ExpectSuccess(spec, "({e: 'alpha', sub: 1})", "{'e':'alpha','sub':1}");
441 ExpectSuccess(spec, "({e: 'beta', sub: 1})", "{'e':'beta','sub':1}");
442 ExpectFailure(spec, "({e: 'gamma', sub: 1})",
443 PropertyError("e", InvalidEnumValue(valid_enums)));
444 ExpectFailure(spec, "({e: 'alpha'})", MissingRequiredProperty("sub"));
445 }
446
447 {
448 const char kObjectWithRefObjectSpec[] =
449 "{"
450 " 'name': 'objWithRefObject',"
451 " 'type': 'object',"
452 " 'properties': {"
453 " 'o': {'$ref': 'refObj'}"
454 " }"
455 "}";
456 ArgumentSpec spec(*ValueFromString(kObjectWithRefObjectSpec));
457 ExpectSuccess(spec, "({o: {prop1: 'foo'}})", "{'o':{'prop1':'foo'}}");
458 ExpectSuccess(spec, "({o: {prop1: 'foo', prop2: 2}})",
459 "{'o':{'prop1':'foo','prop2':2}}");
460 ExpectFailure(
461 spec, "({o: {prop1: 1}})",
462 PropertyError("o", PropertyError("prop1", InvalidType(kTypeString,
463 kTypeInteger))));
464 }
465
466 {
467 const char kRefEnumListSpec[] =
468 "{'type': 'array', 'items': {'$ref': 'refEnum'}}";
469 ArgumentSpec spec(*ValueFromString(kRefEnumListSpec));
470 ExpectSuccess(spec, "['alpha']", "['alpha']");
471 ExpectSuccess(spec, "['alpha', 'alpha']", "['alpha','alpha']");
472 ExpectSuccess(spec, "['alpha', 'beta']", "['alpha','beta']");
473 ExpectFailure(spec, "['alpha', 'beta', 'gamma']",
474 IndexError(2u, InvalidEnumValue(valid_enums)));
475 }
476 }
477
478 TEST_F(ArgumentSpecUnitTest, TypeChoicesTest) {
479 using namespace api_errors;
480 {
481 const char kSimpleChoices[] =
482 "{'choices': [{'type': 'string'}, {'type': 'integer'}]}";
483 ArgumentSpec spec(*ValueFromString(kSimpleChoices));
484 ExpectSuccess(spec, "'alpha'", "'alpha'");
485 ExpectSuccess(spec, "42", "42");
486 ExpectFailure(spec, "true", InvalidChoice());
487 }
488
489 {
490 const char kComplexChoices[] =
491 "{"
492 " 'choices': ["
493 " {'type': 'array', 'items': {'type': 'string'}},"
494 " {'type': 'object', 'properties': {'prop1': {'type': 'string'}}}"
495 " ]"
496 "}";
497 ArgumentSpec spec(*ValueFromString(kComplexChoices));
498 ExpectSuccess(spec, "['alpha']", "['alpha']");
499 ExpectSuccess(spec, "['alpha', 'beta']", "['alpha','beta']");
500 ExpectSuccess(spec, "({prop1: 'alpha'})", "{'prop1':'alpha'}");
501 ExpectFailure(spec, "({prop1: 1})", InvalidChoice());
502 ExpectFailure(spec, "'alpha'", InvalidChoice());
503 ExpectFailure(spec, "42", InvalidChoice());
504 }
505 }
506
507 TEST_F(ArgumentSpecUnitTest, AdditionalPropertiesTest) {
508 using namespace api_errors;
509 {
510 const char kOnlyAnyAdditionalProperties[] =
511 "{"
512 " 'type': 'object',"
513 " 'additionalProperties': {'type': 'any'}"
514 "}";
515 ArgumentSpec spec(*ValueFromString(kOnlyAnyAdditionalProperties));
516 ExpectSuccess(spec, "({prop1: 'alpha', prop2: 42, prop3: {foo: 'bar'}})",
517 "{'prop1':'alpha','prop2':42,'prop3':{'foo':'bar'}}");
518 ExpectSuccess(spec, "({})", "{}");
519 // Test some crazy keys.
520 ExpectSuccess(spec,
521 "var x = {};\n"
522 "var y = {prop1: 'alpha'};\n"
523 "y[42] = 'beta';\n"
524 "y[x] = 'gamma';\n"
525 "y[undefined] = 'delta';\n"
526 "y;",
527 "{'42':'beta','[object Object]':'gamma','prop1':'alpha',"
528 "'undefined':'delta'}");
529 // We (typically*, see "Fun case" below) don't serialize properties on an
530 // object prototype.
531 ExpectSuccess(spec,
532 "({\n"
533 " __proto__: {protoProp: 'proto'},\n"
534 " instanceProp: 'instance'\n"
535 "})",
536 "{'instanceProp':'instance'}");
537 // Fun case: Remove a property as a result of getting another. Currently,
538 // we don't check each property with HasOwnProperty() during iteration, so
539 // Fun case: Remove a property as a result of getting another. Currently,
540 // we don't check each property with HasOwnProperty() during iteration, so
541 // we still try to serialize it. But we don't serialize undefined, so in the
542 // case of the property not being defined on the prototype, this works as
543 // expected.
544 ExpectSuccess(spec,
545 "var x = {};\n"
546 "Object.defineProperty(\n"
547 " x, 'alpha',\n"
548 " {\n"
549 " enumerable: true,\n"
550 " get: () => { delete x.omega; return 'alpha'; }\n"
551 " });\n"
552 "x.omega = 'omega';\n"
553 "x;",
554 "{'alpha':'alpha'}");
555 // Fun case continued: If an object removes the property, and the property
556 // *is* present on the prototype, then we serialize the value from the
557 // prototype. This is inconsistent, but only manifests scripts are doing
558 // crazy things (and is still safe).
559 // TODO(devlin): We *could* add a HasOwnProperty() check, in which case
560 // the result of this call should be {'alpha':'alpha'}.
561 ExpectSuccess(spec,
562 "var x = {\n"
563 " __proto__: { omega: 'different omega' }\n"
564 "};\n"
565 "Object.defineProperty(\n"
566 " x, 'alpha',\n"
567 " {\n"
568 " enumerable: true,\n"
569 " get: () => { delete x.omega; return 'alpha'; }\n"
570 " });\n"
571 "x.omega = 'omega';\n"
572 "x;",
573 "{'alpha':'alpha','omega':'different omega'}");
574 }
575 {
576 const char kPropertiesAndAnyAdditionalProperties[] =
577 "{"
578 " 'type': 'object',"
579 " 'properties': {"
580 " 'prop1': {'type': 'string'}"
581 " },"
582 " 'additionalProperties': {'type': 'any'}"
583 "}";
584 ArgumentSpec spec(*ValueFromString(kPropertiesAndAnyAdditionalProperties));
585 ExpectSuccess(spec, "({prop1: 'alpha', prop2: 42, prop3: {foo: 'bar'}})",
586 "{'prop1':'alpha','prop2':42,'prop3':{'foo':'bar'}}");
587 // Additional properties are optional.
588 ExpectSuccess(spec, "({prop1: 'foo'})", "{'prop1':'foo'}");
589 ExpectFailure(spec, "({prop2: 42, prop3: {foo: 'bar'}})",
590 MissingRequiredProperty("prop1"));
591 ExpectFailure(
592 spec, "({prop1: 42})",
593 PropertyError("prop1", InvalidType(kTypeString, kTypeInteger)));
594 }
595 {
596 const char kTypedAdditionalProperties[] =
597 "{"
598 " 'type': 'object',"
599 " 'additionalProperties': {'type': 'string'}"
600 "}";
601 ArgumentSpec spec(*ValueFromString(kTypedAdditionalProperties));
602 ExpectSuccess(spec, "({prop1: 'alpha', prop2: 'beta', prop3: 'gamma'})",
603 "{'prop1':'alpha','prop2':'beta','prop3':'gamma'}");
604 ExpectFailure(
605 spec, "({prop1: 'alpha', prop2: 42})",
606 PropertyError("prop2", InvalidType(kTypeString, kTypeInteger)));
607 }
608 }
609
610 TEST_F(ArgumentSpecUnitTest, InstanceOfTest) {
611 using namespace api_errors;
612 {
613 const char kInstanceOfRegExp[] =
614 "{"
615 " 'type': 'object',"
616 " 'isInstanceOf': 'RegExp'"
617 "}";
618 ArgumentSpec spec(*ValueFromString(kInstanceOfRegExp));
619 ExpectSuccess(spec, "(new RegExp())", "{}");
620 ExpectSuccess(spec, "({ __proto__: RegExp.prototype })", "{}");
621 ExpectSuccess(spec,
622 "(function() {\n"
623 " function subRegExp() {}\n"
624 " subRegExp.prototype = { __proto__: RegExp.prototype };\n"
625 " return new subRegExp();\n"
626 "})()",
627 "{}");
628 ExpectSuccess(spec,
629 "(function() {\n"
630 " function RegExp() {}\n"
631 " return new RegExp();\n"
632 "})()",
633 "{}");
634 ExpectFailure(spec, "({})", NotAnInstance("RegExp"));
635 ExpectFailure(spec, "('')", InvalidType("RegExp", kTypeString));
636 ExpectFailure(spec, "('.*')", InvalidType("RegExp", kTypeString));
637 ExpectFailure(spec, "({ __proto__: Date.prototype })",
638 NotAnInstance("RegExp"));
639 }
640
641 {
642 const char kInstanceOfCustomClass[] =
643 "{"
644 " 'type': 'object',"
645 " 'isInstanceOf': 'customClass'"
646 "}";
647 ArgumentSpec spec(*ValueFromString(kInstanceOfCustomClass));
648 ExpectSuccess(spec,
649 "(function() {\n"
650 " function customClass() {}\n"
651 " return new customClass();\n"
652 "})()",
653 "{}");
654 ExpectSuccess(spec,
655 "(function() {\n"
656 " function customClass() {}\n"
657 " function otherClass() {}\n"
658 " otherClass.prototype = \n"
659 " { __proto__: customClass.prototype };\n"
660 " return new otherClass();\n"
661 "})()",
662 "{}");
663 ExpectFailure(spec, "({})", NotAnInstance("customClass"));
664 ExpectFailure(spec,
665 "(function() {\n"
666 " function otherClass() {}\n"
667 " return new otherClass();\n"
668 "})()",
669 NotAnInstance("customClass"));
670 }
671 }
672
673 TEST_F(ArgumentSpecUnitTest, MinAndMaxLengths) {
674 using namespace api_errors;
675 {
676 const char kMinLengthString[] = "{'type': 'string', 'minLength': 3}";
677 ArgumentSpec spec(*ValueFromString(kMinLengthString));
678 ExpectSuccess(spec, "'aaa'", "'aaa'");
679 ExpectSuccess(spec, "'aaaa'", "'aaaa'");
680 ExpectFailure(spec, "'aa'", TooFewStringChars(3, 2));
681 ExpectFailure(spec, "''", TooFewStringChars(3, 0));
682 }
683
684 {
685 const char kMaxLengthString[] = "{'type': 'string', 'maxLength': 3}";
686 ArgumentSpec spec(*ValueFromString(kMaxLengthString));
687 ExpectSuccess(spec, "'aaa'", "'aaa'");
688 ExpectSuccess(spec, "'aa'", "'aa'");
689 ExpectSuccess(spec, "''", "''");
690 ExpectFailure(spec, "'aaaa'", TooManyStringChars(3, 4));
691 }
692
693 {
694 const char kMinLengthArray[] =
695 "{'type': 'array', 'items': {'type': 'integer'}, 'minItems': 3}";
696 ArgumentSpec spec(*ValueFromString(kMinLengthArray));
697 ExpectSuccess(spec, "[1, 2, 3]", "[1,2,3]");
698 ExpectSuccess(spec, "[1, 2, 3, 4]", "[1,2,3,4]");
699 ExpectFailure(spec, "[1, 2]", TooFewArrayItems(3, 2));
700 ExpectFailure(spec, "[]", TooFewArrayItems(3, 0));
701 }
702
703 {
704 const char kMaxLengthArray[] =
705 "{'type': 'array', 'items': {'type': 'integer'}, 'maxItems': 3}";
706 ArgumentSpec spec(*ValueFromString(kMaxLengthArray));
707 ExpectSuccess(spec, "[1, 2, 3]", "[1,2,3]");
708 ExpectSuccess(spec, "[1, 2]", "[1,2]");
709 ExpectSuccess(spec, "[]", "[]");
710 ExpectFailure(spec, "[1, 2, 3, 4]", TooManyArrayItems(3, 4));
711 }
712 }
713
714 TEST_F(ArgumentSpecUnitTest, PreserveNull) {
715 using namespace api_errors;
716 {
717 const char kObjectSpec[] =
718 "{"
719 " 'type': 'object',"
720 " 'additionalProperties': {'type': 'any'},"
721 " 'preserveNull': true"
722 "}";
723 ArgumentSpec spec(*ValueFromString(kObjectSpec));
724 ExpectSuccess(spec, "({foo: 1, bar: null})", "{'bar':null,'foo':1}");
725 // Subproperties shouldn't preserve null (if not specified).
726 ExpectSuccess(spec, "({prop: {subprop1: 'foo', subprop2: null}})",
727 "{'prop':{'subprop1':'foo'}}");
728 }
729
730 {
731 const char kObjectSpec[] =
732 "{"
733 " 'type': 'object',"
734 " 'additionalProperties': {'type': 'any', 'preserveNull': true},"
735 " 'preserveNull': true"
736 "}";
737 ArgumentSpec spec(*ValueFromString(kObjectSpec));
738 ExpectSuccess(spec, "({foo: 1, bar: null})", "{'bar':null,'foo':1}");
739 // Here, subproperties should preserve null.
740 ExpectSuccess(spec, "({prop: {subprop1: 'foo', subprop2: null}})",
741 "{'prop':{'subprop1':'foo','subprop2':null}}");
742 }
743
744 {
745 const char kObjectSpec[] =
746 "{"
747 " 'type': 'object',"
748 " 'properties': {'prop1': {'type': 'string', 'optional': true}},"
749 " 'preserveNull': true"
750 "}";
751 ArgumentSpec spec(*ValueFromString(kObjectSpec));
752 ExpectSuccess(spec, "({})", "{}");
753 ExpectSuccess(spec, "({prop1: null})", "{'prop1':null}");
754 ExpectSuccess(spec, "({prop1: 'foo'})", "{'prop1':'foo'}");
755 // Undefined should not be preserved.
756 ExpectSuccess(spec, "({prop1: undefined})", "{}");
757 // preserveNull shouldn't affect normal parsing restrictions.
758 ExpectFailure(
759 spec, "({prop1: 1})",
760 PropertyError("prop1", InvalidType(kTypeString, kTypeInteger)));
761 }
762 }
763
764 TEST_F(ArgumentSpecUnitTest, NaNFun) {
765 using namespace api_errors;
766
767 {
768 const char kAnySpec[] = "{'type': 'any'}";
769 ArgumentSpec spec(*ValueFromString(kAnySpec));
770 ExpectFailure(spec, "NaN", UnserializableValue());
771 }
772
773 {
774 const char kObjectWithAnyPropertiesSpec[] =
775 "{'type': 'object', 'additionalProperties': {'type': 'any'}}";
776 ArgumentSpec spec(*ValueFromString(kObjectWithAnyPropertiesSpec));
777 ExpectSuccess(spec, "({foo: NaN, bar: 'baz'})", "{'bar':'baz'}");
778 }
779 }
780
781 TEST_F(ArgumentSpecUnitTest, GetTypeName) {
782 struct {
783 ArgumentType type;
784 const char* expected_type_name;
785 } simple_cases[] = {
786 {ArgumentType::BOOLEAN, api_errors::kTypeBoolean},
787 {ArgumentType::INTEGER, api_errors::kTypeInteger},
788 {ArgumentType::OBJECT, api_errors::kTypeObject},
789 {ArgumentType::LIST, api_errors::kTypeList},
790 {ArgumentType::BINARY, api_errors::kTypeBinary},
791 {ArgumentType::FUNCTION, api_errors::kTypeFunction},
792 {ArgumentType::ANY, api_errors::kTypeAny},
793 };
794
795 for (const auto& test_case : simple_cases) {
796 ArgumentSpec spec(test_case.type);
797 EXPECT_EQ(test_case.expected_type_name, spec.GetTypeName());
798 }
799
800 {
801 const char kRefName[] = "someRef";
802 ArgumentSpec ref_spec(ArgumentType::REF);
803 ref_spec.set_ref(kRefName);
804 EXPECT_EQ(kRefName, ref_spec.GetTypeName());
805 }
806
807 {
808 std::vector<std::unique_ptr<ArgumentSpec>> choices;
809 choices.push_back(base::MakeUnique<ArgumentSpec>(ArgumentType::INTEGER));
810 choices.push_back(base::MakeUnique<ArgumentSpec>(ArgumentType::STRING));
811 ArgumentSpec choices_spec(ArgumentType::CHOICES);
812 choices_spec.set_choices(std::move(choices));
813 EXPECT_EQ("[integer|string]", choices_spec.GetTypeName());
814 }
815 }
816
817 } // namespace extensions
OLDNEW
« no previous file with comments | « extensions/renderer/argument_spec_builder.cc ('k') | extensions/renderer/binding_access_checker.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698