diff options
-rwxr-xr-x | tests/embind/embind.test.js | 1495 | ||||
-rw-r--r-- | tests/embind/embind_test.cpp | 1651 | ||||
-rw-r--r-- | tests/embind/embind_test.js | 398 | ||||
-rwxr-xr-x | tests/embind/imvu_test_adapter.js | 614 | ||||
-rwxr-xr-x | tests/embind/underscore-1.4.2.js | 1200 | ||||
-rwxr-xr-x | tests/runner.py | 54 |
6 files changed, 4900 insertions, 512 deletions
diff --git a/tests/embind/embind.test.js b/tests/embind/embind.test.js new file mode 100755 index 00000000..94d935f2 --- /dev/null +++ b/tests/embind/embind.test.js @@ -0,0 +1,1495 @@ +module({ + Emscripten: '../../../../build/embind_test.js', +}, function(imports) { + var cm = imports.Emscripten; + + var CheckForLeaks = fixture("check for leaks", function() { + this.setUp(function() { + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + cm._mallocDebug(2); + assert.equal(0, cm.count_emval_handles()); + cm._mallocAssertAllMemoryFree(); + } + }); + this.tearDown(function() { + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + cm._mallocAssertAllMemoryFree(); + assert.equal(0, cm.count_emval_handles()); + } + }); + }); + + var BaseFixture = CheckForLeaks; + + BaseFixture.extend("temp jig", function() { + test("temp test", function() { + }); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + + BaseFixture.extend("leak testing", function() { + test("no memory allocated at start of test", function() { + cm._mallocAssertAllMemoryFree(); + }); + test("assert when memory is allocated", function() { + var ptr = cm._malloc(42); + assert.throws(cm._MemoryAllocationError, function() { + cm._mallocAssertAllMemoryFree(); + }); + cm._free(ptr); + }); + test("allocated memory counts down again for free", function() { + var ptr = cm._malloc(42); + cm._free(ptr); + cm._mallocAssertAllMemoryFree(); + }); + test("free without malloc throws MemoryAllocationError", function() { + var ptr = cm._malloc(42); + cm._free(ptr); + assert.throws(cm._MemoryAllocationError, function() { + cm._free(ptr); + }); + }); + }); + + } + + BaseFixture.extend("access to base class members", function() { + test("method name in derived class silently overrides inherited name", function() { + var derived = new cm.Derived(); + assert.equal("Derived", derived.getClassName()); + derived.delete(); + }); + test("can reference base method from derived class", function(){ + var derived = new cm.Derived(); + assert.equal("Base", derived.getClassNameFromBase()); + derived.delete(); + }); + test("can reference base method from doubly derived class", function() { + var derivedTwice = new cm.DerivedTwice(); + assert.equal("Base", derivedTwice.getClassNameFromBase()); + derivedTwice.delete(); + }); + test("can reference base method through unbound classes", function() { + var derivedThrice = new cm.DerivedThrice(); + assert.equal("Base", derivedThrice.getClassNameFromBase()); + derivedThrice.delete(); + }); + test("property name in derived class hides identically named property in base class for set", function() { + var derived = new cm.Derived(); + derived.setMember(7); + + derived.member = 17; + + assert.equal(17, derived.getMember()); + derived.delete(); + }); + test("can reference base property from derived class for get", function(){ + var derived = new cm.Derived(); + derived.setBaseMember(5); + + assert.equal(5, derived.baseMember); + + derived.delete(); + }); + test("can reference property of any base class for get when multiply derived", function(){ + var derived = new cm.MultiplyDerived(); + derived.setBaseMember(11); + + assert.equal(11, derived.baseMember); + + derived.delete(); + }); + test("can reference base property from derived class for set", function(){ + var derived = new cm.Derived(); + + derived.baseMember = 32; + + assert.equal(32, derived.getBaseMember()); + derived.delete(); + }); + test("can reference property of any base for set when multiply derived", function(){ + var derived = new cm.MultiplyDerived(); + derived.setBaseMember(97); + + derived.baseMember = 32; + + assert.equal(32, derived.getBaseMember()); + derived.delete(); + }); + test("can reach around derived property to access base property with same name for get", function() { + var derived = new cm.Derived(); + derived.setMember(12); + derived.delete(); + }); + + test("if deriving from second base adjusts pointer", function() { + var derived = new cm.HasTwoBases; + assert.equal("Base2", derived.getField()); + derived.delete(); + }); + + test("properties adjust pointer", function() { + var derived = new cm.HasTwoBases; + derived.field = "Foo"; + assert.equal("Foo", derived.getField()); + assert.equal("Foo", derived.field); + derived.delete(); + }); + + test("calling method on unrelated class throws error", function() { + var a = new cm.HasTwoBases; + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call(a, "foo"); + }); + assert.equal('Derived.setMember incompatible with "this" of type HasTwoBases', e.message); + a.delete(); + }); + + test("calling method with invalid this throws error", function() { + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call(undefined, "foo"); + }); + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + // got Error: expected: Derived.setMember with invalid "this": undefined, actual: Derived.setMember incompatible with "this" of type Object + assert.equal('Derived.setMember with invalid "this": undefined', e.message); + } + + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call("this", "foo"); + }); + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + // TODO got 'Derived.setMember incompatible with "this" of type Object' + assert.equal('Derived.setMember with invalid "this": this', e.message); + } + + var e = assert.throws(cm.BindingError, function() { + cm.Derived.prototype.setMember.call({}, "foo"); + }); + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + assert.equal('Derived.setMember incompatible with "this" of type Object', e.message); + } + }); + + test("setting and getting property on unrelated class throws error", function() { + var a = new cm.HasTwoBases; + var e = assert.throws(cm.BindingError, function() { + Object.getOwnPropertyDescriptor(cm.HeldBySmartPtr.prototype, 'i').set.call(a, 10); + }); + assert.equal('HeldBySmartPtr.i setter incompatible with "this" of type HasTwoBases', e.message); + + var e = assert.throws(cm.BindingError, function() { + Object.getOwnPropertyDescriptor(cm.HeldBySmartPtr.prototype, 'i').get.call(a); + }); + assert.equal('HeldBySmartPtr.i getter incompatible with "this" of type HasTwoBases', e.message); + + a.delete(); + }); + }); + + BaseFixture.extend("automatic upcasting of parameters passed to C++", function() { + // raw + test("raw pointer argument is upcast to parameter type", function() { + var derived = new cm.Derived(); + var name = cm.embind_test_get_class_name_via_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("automatic raw pointer upcasting works with multiple inheritance", function() { + var derived = new cm.MultiplyDerived(); + var name = cm.embind_test_get_class_name_via_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("automatic raw pointer upcasting does not change local pointer", function() { + var derived = new cm.MultiplyDerived(); + cm.embind_test_get_class_name_via_base_ptr(derived); + assert.equal("MultiplyDerived", derived.getClassName()); + derived.delete(); + }); + + test("passing incompatible raw pointer to method throws exception", function() { + var base = new cm.Base(); + assert.throws(cm.BindingError, function() { + cm.embind_test_get_class_name_via_second_base_ptr(base); + }); + base.delete(); + }); + + // raw polymorphic + test("polymorphic raw pointer argument is upcast to parameter type", function() { + var derived = new cm.PolyDerived(); + var name = cm.embind_test_get_class_name_via_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + + test("automatic polymorphic raw pointer upcasting works with multiple inheritance", function() { + var derived = new cm.PolyMultiplyDerived(); + var name = cm.embind_test_get_class_name_via_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + + test("passing incompatible raw polymorphic pointer to method throws exception", function() { + var base = new cm.PolyBase(); + assert.throws(cm.BindingError, function() { + cm.embind_test_get_class_name_via_polymorphic_second_base_ptr(base); + }); + base.delete(); + + }); + + // smart + test("can pass smart pointer to raw pointer parameter", function() { + var smartBase = cm.embind_test_return_smart_base_ptr(); + assert.equal("Base", cm.embind_test_get_class_name_via_base_ptr(smartBase)); + smartBase.delete(); + }); + + test("can pass and upcast smart pointer to raw pointer parameter", function() { + var smartDerived = cm.embind_test_return_smart_derived_ptr(); + assert.equal("Base", cm.embind_test_get_class_name_via_base_ptr(smartDerived)); + smartDerived.delete(); + }); + + test("smart pointer argument is upcast to parameter type", function() { + var derived = cm.embind_test_return_smart_derived_ptr(); + assert.instanceof(derived, cm.Derived); + assert.instanceof(derived, cm.Base); + var name = cm.embind_test_get_class_name_via_smart_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("return smart derived ptr as base", function() { + var derived = cm.embind_test_return_smart_derived_ptr_as_base(); + assert.equal("PolyDerived", cm.embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(derived)); + assert.equal("PolyDerived", derived.getClassName()); + derived.delete(); + }); + + test("return smart derived ptr as val", function() { + var derived = cm.embind_test_return_smart_derived_ptr_as_val(); + assert.equal("PolyDerived", cm.embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(derived)); + derived.delete(); + }); + + test("automatic smart pointer upcasting works with multiple inheritance", function() { + var derived = cm.embind_test_return_smart_multiply_derived_ptr(); + var name = cm.embind_test_get_class_name_via_smart_base_ptr(derived); + assert.equal("Base", name); + derived.delete(); + }); + + test("automatically upcasted smart pointer parameter shares ownership with original argument", function() { + var derived = cm.embind_test_return_smart_multiply_derived_ptr(); + assert.equal(1, cm.MultiplyDerived.getInstanceCount()); + cm.embind_save_smart_base_pointer(derived); + assert.equal(1, cm.MultiplyDerived.getInstanceCount()); + derived.delete(); + assert.equal(1, cm.MultiplyDerived.getInstanceCount()); + cm.embind_save_smart_base_pointer(null); + assert.equal(0, cm.MultiplyDerived.getInstanceCount()); + }); + + // smart polymorphic + test("smart polymorphic pointer argument is upcast to parameter type", function() { + var derived = cm.embind_test_return_smart_polymorphic_derived_ptr(); + var name = cm.embind_test_get_class_name_via_smart_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + + test("automatic smart polymorphic pointer upcasting works with multiple inheritance", function() { + var derived = cm.embind_test_return_smart_polymorphic_multiply_derived_ptr(); + var name = cm.embind_test_get_class_name_via_smart_polymorphic_base_ptr(derived); + assert.equal("PolyBase", name); + derived.delete(); + }); + }); + + BaseFixture.extend("automatic downcasting of return values received from C++", function() { + // raw + test("non-polymorphic raw pointers are not downcast and do not break automatic casting mechanism", function() { + var base = cm.embind_test_return_raw_derived_ptr_as_base(); + assert.equal("Base", base.getClassName()); + assert.instanceof(base, cm.Base); + base.delete(); + }); + + // raw polymorphic + test("polymorphic raw pointer return value is downcast to allocated type (if that is bound)", function() { + var derived = cm.embind_test_return_raw_polymorphic_derived_ptr_as_base(); + assert.instanceof(derived, cm.PolyBase); + assert.instanceof(derived, cm.PolyDerived); + assert.equal("PolyDerived", derived.getClassName()); + var siblingDerived = cm.embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base(); + assert.equal("PolySiblingDerived", siblingDerived.getClassName()); + siblingDerived.delete(); + derived.delete(); + }); + + test("polymorphic raw pointer return value is downcast to the most derived bound type", function() { + var derivedThrice = cm.embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base(); + // if the actual returned type is not bound, then don't assume anything + assert.equal("PolyBase", derivedThrice.getClassName()); + // if we ever fix this, then reverse the assertion + //assert.equal("PolyDerivedThrice", derivedThrice.getClassName()); + derivedThrice.delete(); + }); + + test("polymorphic smart pointer return value is downcast to the most derived type which has an associated smart pointer", function() { + var derived = cm.embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base(); + // if the actual returned type is not bound, then don't assume anything + assert.equal("PolyBase", derived.getClassName()); + // if we ever fix this, then remove the assertion + //assert.equal("PolyDerived", derived.getClassName()); + derived.delete(); + }); + + test("automatic downcasting works with multiple inheritance", function() { + var base = cm.embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base(); + var secondBase = cm.embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base(); + assert.equal("PolyMultiplyDerived", base.getClassName()); + // embind does not support multiple inheritance + //assert.equal("PolyMultiplyDerived", secondBase.getClassName()); + secondBase.delete(); + base.delete(); + }); + + // smart + test("non-polymorphic smart pointers do not break automatic casting mechanism", function() { + }); + + // smart polymorphic + test("automatically downcasting a smart pointer does not change the underlying pointer", function() { + cm.PolyDerived.setPtrDerived(); + assert.equal("PolyBase", cm.PolyDerived.getPtrClassName()); + var derived = cm.PolyDerived.getPtr(); + assert.equal("PolyDerived", derived.getClassName()); + assert.equal("PolyBase", cm.PolyDerived.getPtrClassName()); + derived.delete(); + cm.PolyDerived.releasePtr(); + }); + + test("polymorphic smart pointer return value is actual allocated type (when bound)", function() { + var derived = cm.embind_test_return_smart_polymorphic_derived_ptr_as_base(); + assert.equal("PolyDerived", derived.getClassName()); + + var siblingDerived = cm.embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base(); + assert.equal("PolySiblingDerived", siblingDerived.getClassName()); + + siblingDerived.delete(); + derived.delete(); + }); + }); + + BaseFixture.extend("embind", function() { + test("value creation", function() { + assert.equal(15, cm.emval_test_new_integer()); + assert.equal("Hello everyone", cm.emval_test_new_string()); + assert.equal("Hello everyone", cm.emval_test_get_string_from_val({key: "Hello everyone"})); + + var object = cm.emval_test_new_object(); + assert.equal('bar', object.foo); + assert.equal(1, object.baz); + }); + + test("pass const reference to primitive", function() { + assert.equal(3, cm.const_ref_adder(1, 2)); + }); + + test("passthrough", function() { + var a = {foo: 'bar'}; + var b = cm.emval_test_passthrough(a); + a.bar = 'baz'; + assert.equal('baz', b.bar); + + assert.equal(0, cm.count_emval_handles()); + }); + + test("void return converts to undefined", function() { + assert.equal(undefined, cm.emval_test_return_void()); + }); + + test("booleans can be marshalled", function() { + assert.equal(false, cm.emval_test_not(true)); + assert.equal(true, cm.emval_test_not(false)); + }); + + test("convert double to unsigned", function() { + var rv = cm.emval_test_as_unsigned(1.5); + assert.equal('number', typeof rv); + assert.equal(1, rv); + assert.equal(0, cm.count_emval_handles()); + }); + + test("get length of array", function() { + assert.equal(10, cm.emval_test_get_length([0, 1, 2, 3, 4, 5, 'a', 'b', 'c', 'd'])); + assert.equal(0, cm.count_emval_handles()); + }); + + test("add a bunch of things", function() { + assert.equal(66.0, cm.emval_test_add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); + assert.equal(0, cm.count_emval_handles()); + }); + + test("sum array", function() { + assert.equal(66, cm.emval_test_sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])); + assert.equal(0, cm.count_emval_handles()); + }); + + test("strings", function() { + assert.equal("foobar", "foo" + "bar"); + assert.equal("foobar", cm.emval_test_take_and_return_std_string("foobar")); + + assert.equal("foobar", cm.emval_test_take_and_return_std_string_const_ref("foobar")); + }); + + test("nuls pass through strings", function() { + assert.equal("foo\0bar", cm.emval_test_take_and_return_std_string("foo\0bar")); + }); + + test("no memory leak when passing strings in by const reference", function() { + cm.emval_test_take_and_return_std_string_const_ref("foobar"); + }); + + test("can create new object", function() { + assert.deepEqual({}, cm.embind_test_new_Object()); + }); + + test("can invoke constructors with arguments", function() { + function constructor(i, s, argument) { + this.i = i; + this.s = s; + this.argument = argument; + } + constructor.prototype.method = function() { + return this.argument; + }; + var x = {}; + var instance = cm.embind_test_new_factory(constructor, x); + assert.equal(10, instance.i); + assert.equal("hello", instance.s); + assert.equal(x, instance.argument); + }); + + test("can return module property objects", function() { + assert.equal(cm.HEAP8, cm.get_module_property("HEAP8")); + }); + + test("can return big class instances", function() { + var c = cm.embind_test_return_big_class_instance(); + assert.equal(11, c.member); + c.delete(); + }); + + test("can return small class instances", function() { + var c = cm.embind_test_return_small_class_instance(); + assert.equal(7, c.member); + c.delete(); + }); + + test("can pass small class instances", function() { + var c = new cm.SmallClass(); + var m = cm.embind_test_accept_small_class_instance(c); + assert.equal(7, m); + c.delete(); + }); + + test("can pass big class instances", function() { + var c = new cm.BigClass(); + var m = cm.embind_test_accept_big_class_instance(c); + assert.equal(11, m); + c.delete(); + }); + + test("can get member classes then call its member functions", function() { + var p = new cm.ParentClass(); + var c = p.getBigClass(); + var m = c.getMember(); + assert.equal(11, m); + c.delete(); + p.delete(); + }); + + test('C++ -> JS primitive type range checks', function() { + // all types should have zero. + assert.equal("0", cm.char_to_string(0)); + assert.equal("0", cm.signed_char_to_string(0)); + assert.equal("0", cm.unsigned_char_to_string(0)); + assert.equal("0", cm.short_to_string(0)); + assert.equal("0", cm.unsigned_short_to_string(0)); + assert.equal("0", cm.int_to_string(0)); + assert.equal("0", cm.unsigned_int_to_string(0)); + assert.equal("0", cm.long_to_string(0)); + assert.equal("0", cm.unsigned_long_to_string(0)); + + // all types should have positive values. + assert.equal("5", cm.char_to_string(5)); + assert.equal("5", cm.signed_char_to_string(5)); + assert.equal("5", cm.unsigned_char_to_string(5)); + assert.equal("5", cm.short_to_string(5)); + assert.equal("5", cm.unsigned_short_to_string(5)); + assert.equal("5", cm.int_to_string(5)); + assert.equal("5", cm.unsigned_int_to_string(5)); + assert.equal("5", cm.long_to_string(5)); + assert.equal("5", cm.unsigned_long_to_string(5)); + + // signed types should have negative values. + assert.equal("-5", cm.char_to_string(-5)); // Assuming char as signed. + assert.equal("-5", cm.signed_char_to_string(-5)); + assert.equal("-5", cm.short_to_string(-5)); + assert.equal("-5", cm.int_to_string(-5)); + assert.equal("-5", cm.long_to_string(-5)); + + // assumptions: char == signed char == 8 bits + // unsigned char == 8 bits + // short == 16 bits + // int == long == 32 bits + + // all types should have their max positive values. + assert.equal("127", cm.char_to_string(127)); + assert.equal("127", cm.signed_char_to_string(127)); + assert.equal("255", cm.unsigned_char_to_string(255)); + assert.equal("32767", cm.short_to_string(32767)); + assert.equal("65535", cm.unsigned_short_to_string(65535)); + assert.equal("2147483647", cm.int_to_string(2147483647)); + assert.equal("4294967295", cm.unsigned_int_to_string(4294967295)); + assert.equal("2147483647", cm.long_to_string(2147483647)); + assert.equal("4294967295", cm.unsigned_long_to_string(4294967295)); + + // signed types should have their min negative values. + assert.equal("-128", cm.char_to_string(-128)); + assert.equal("-128", cm.signed_char_to_string(-128)); + assert.equal("-32768", cm.short_to_string(-32768)); + assert.equal("-2147483648", cm.int_to_string(-2147483648)); + assert.equal("-2147483648", cm.long_to_string(-2147483648)); + + // passing out of range values should fail. + assert.throws(TypeError, function() { cm.char_to_string(-129); }); + assert.throws(TypeError, function() { cm.char_to_string(128); }); + assert.throws(TypeError, function() { cm.signed_char_to_string(-129); }); + assert.throws(TypeError, function() { cm.signed_char_to_string(128); }); + assert.throws(TypeError, function() { cm.unsigned_char_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_char_to_string(256); }); + assert.throws(TypeError, function() { cm.short_to_string(-32769); }); + assert.throws(TypeError, function() { cm.short_to_string(32768); }); + assert.throws(TypeError, function() { cm.unsigned_short_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_short_to_string(65536); }); + assert.throws(TypeError, function() { cm.int_to_string(-2147483649); }); + assert.throws(TypeError, function() { cm.int_to_string(2147483648); }); + assert.throws(TypeError, function() { cm.unsigned_int_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_int_to_string(4294967296); }); + assert.throws(TypeError, function() { cm.long_to_string(-2147483649); }); + assert.throws(TypeError, function() { cm.long_to_string(2147483648); }); + assert.throws(TypeError, function() { cm.unsigned_long_to_string(-1); }); + assert.throws(TypeError, function() { cm.unsigned_long_to_string(4294967296); }); + + }); + +/* + test("can get templated member classes then call its member functions", function() { + var p = new cm.ContainsTemplatedMemberClass(); + var c = p.getTestTemplate(); + var m = c.getMember(1); + assert.equal(87, m); + c.delete(); + p.delete(); + }); +*/ + }); + + BaseFixture.extend("vector", function() { + test("std::vector returns as an native object", function() { + var vec = cm.emval_test_return_vector(); + + assert.equal(3, vec.size()); + assert.equal(10, vec.get(0)); + assert.equal(20, vec.get(1)); + assert.equal(30, vec.get(2)); + vec.delete(); + }); + + test("out of bounds std::vector access returns undefined", function() { + var vec = cm.emval_test_return_vector(); + + assert.throws(TypeError, function() { vec.get(-1); }); + assert.equal(undefined, vec.get(4)); + + vec.delete(); + }); + + test("std::vector<std::shared_ptr<>> can be passed back", function() { + var vec = cm.emval_test_return_shared_ptr_vector(); + + assert.equal(2, vec.size()); + var str0 = vec.get(0); + var str1 = vec.get(1); + + assert.equal('string #1', str0.get()); + assert.equal('string #2', str1.get()); + str0.delete(); + str1.delete(); + + vec.delete(); + }); + + test("objects can be pushed back", function() { + var vectorHolder = new cm.VectorHolder(); + var vec = vectorHolder.get(); + assert.equal(2, vec.size()); + + var str = new cm.StringHolder('abc'); + vec.push_back(str); + str.delete(); + assert.equal(3, vec.size()); + var str = vec.get(2); + assert.equal('abc', str.get()); + + str.delete(); + vec.delete(); + vectorHolder.delete(); + }); + + test("can get elements with array operator", function(){ + var vec = cm.emval_test_return_vector(); + assert.equal(10, vec.get(0)); + vec.delete(); + }); + + test("can set elements with array operator", function() { + var vec = cm.emval_test_return_vector(); + assert.equal(10, vec.get(0)); + vec.set(2, 60); + assert.equal(60, vec.get(2)); + vec.delete(); + }); + + test("can set and get objects", function() { + var vec = cm.emval_test_return_shared_ptr_vector(); + var str = vec.get(0); + assert.equal('string #1', str.get()); + str.delete(); + vec.delete(); + }); + }); + + BaseFixture.extend("map", function() { + test("std::map returns as native object", function() { + var map = cm.embind_test_get_string_int_map(); + + assert.equal(2, map.size()); + assert.equal(1, map.get("one")); + assert.equal(2, map.get("two")); + + map.delete(); + }); + + test("std::map can set keys and values", function() { + var map = cm.embind_test_get_string_int_map(); + + assert.equal(2, map.size()); + + map.set("three", 3); + + assert.equal(3, map.size()); + assert.equal(3, map.get("three")); + + map.set("three", 4); + + assert.equal(3, map.size()); + assert.equal(4, map.get("three")); + + map.delete(); + }); + }); + + BaseFixture.extend("functors", function() { + test("can get and call function ptrs", function() { + var ptr = cm.emval_test_get_function_ptr(); + assert.equal("foobar", ptr.opcall("foobar")); + ptr.delete(); + }); + + test("can pass functor to C++", function() { + var ptr = cm.emval_test_get_function_ptr(); + assert.equal("asdf", cm.emval_test_take_and_call_functor(ptr)); + ptr.delete(); + }); + + test("can clone handles", function() { + var a = cm.emval_test_get_function_ptr(); + assert.equal(1, a.$$.count.value); + var b = a.clone(); + assert.equal(2, a.$$.count.value); + assert.equal(2, b.$$.count.value); + a.delete(); + + assert.throws(cm.BindingError, function() { + a.delete(); + }); + b.delete(); + }); + }); + + BaseFixture.extend("classes", function() { + test("class instance", function() { + var a = {foo: 'bar'}; + assert.equal(0, cm.count_emval_handles()); + var c = new cm.ValHolder(a); + assert.equal(1, cm.count_emval_handles()); + assert.equal('bar', c.getVal().foo); + assert.equal(1, cm.count_emval_handles()); + + c.setVal('1234'); + assert.equal('1234', c.getVal()); + + c.delete(); + assert.equal(0, cm.count_emval_handles()); + }); + + test("class instance $$ property is non-enumerable", function() { + var c = new cm.ValHolder(undefined); + assert.deepEqual([], Object.keys(c)); + var d = c.clone(); + c.delete(); + + assert.deepEqual([], Object.keys(d)); + d.delete(); + }); + + test("class methods", function() { + assert.equal(10, cm.ValHolder.some_class_method(10)); + + var b = cm.ValHolder.makeValHolder("foo"); + assert.equal("foo", b.getVal()); + b.delete(); + }); + + test("can't call methods on deleted class instances", function() { + var c = new cm.ValHolder(undefined); + c.delete(); + assert.throws(cm.BindingError, function() { + c.getVal(); + }); + assert.throws(cm.BindingError, function() { + c.delete(); + }); + }); + + test("calling constructor without new raises BindingError", function() { + var e = assert.throws(cm.BindingError, function() { + cm.ValHolder(undefined); + }); + assert.equal("Use 'new' to construct ValHolder", e.message); + }); + + test("can return class instances by value", function() { + var c = cm.emval_test_return_ValHolder(); + assert.deepEqual({}, c.getVal()); + c.delete(); + }); + + test("can pass class instances to functions by reference", function() { + var a = {a:1}; + var c = new cm.ValHolder(a); + cm.emval_test_set_ValHolder_to_empty_object(c); + assert.deepEqual({}, c.getVal()); + c.delete(); + }); + + test("can pass smart pointer by reference", function() { + var base = cm.embind_test_return_smart_base_ptr(); + var name = cm.embind_test_get_class_name_via_reference_to_smart_base_ptr(base); + assert.equal("Base", name); + base.delete(); + }); + + test("can pass smart pointer by value", function() { + var base = cm.embind_test_return_smart_base_ptr(); + var name = cm.embind_test_get_class_name_via_smart_base_ptr(base); + assert.equal("Base", name); + base.delete(); + }); + + // todo: fix this + // This test does not work because we make no provision for argument values + // having been changed after returning from a C++ routine invocation. In + // this specific case, the original pointee of the smart pointer was + // freed and replaced by a new one, but the ptr in our local handle + // was never updated after returning from the call. + test("can modify smart pointers passed by reference", function() { +// var base = cm.embind_test_return_smart_base_ptr(); +// cm.embind_modify_smart_pointer_passed_by_reference(base); +// assert.equal("Changed", base.getClassName()); +// base.delete(); + }); + + test("can not modify smart pointers passed by value", function() { + var base = cm.embind_test_return_smart_base_ptr(); + cm.embind_attempt_to_modify_smart_pointer_when_passed_by_value(base); + assert.equal("Base", base.getClassName()); + base.delete(); + }); + + test("const return value", function() { + var c = new cm.ValHolder("foo"); + assert.equal("foo", c.getConstVal()); + c.delete(); + }); + + test("return object by const ref", function() { + var c = new cm.ValHolder("foo"); + assert.equal("foo", c.getValConstRef()); + c.delete(); + }); + + test("instanceof", function() { + var c = new cm.ValHolder("foo"); + assert.instanceof(c, cm.ValHolder); + c.delete(); + }); + + test("can access struct fields", function() { + var c = new cm.CustomStruct(); + assert.equal(10, c.field); + assert.equal(10, c.getField()); + c.delete(); + }); + + test("can set struct fields", function() { + var c = new cm.CustomStruct(); + c.field = 15; + assert.equal(15, c.field); + c.delete(); + }); + + test("assignment returns value", function() { + var c = new cm.CustomStruct(); + assert.equal(15, c.field = 15); + c.delete(); + }); + + test("assigning string to integer raises TypeError", function() { + var c = new cm.CustomStruct(); + + var e = assert.throws(TypeError, function() { + c.field = "hi"; + }); + assert.equal('Cannot convert "hi" to int', e.message); + + var e = assert.throws(TypeError, function() { + c.field = {foo:'bar'}; + }); + assert.equal('Cannot convert "[object Object]" to int', e.message); + + c.delete(); + }); + + test("can return tuples by value", function() { + var c = cm.emval_test_return_TupleVector(); + assert.deepEqual([1, 2, 3], c); + }); + + test("tuples can contain tuples", function() { + var c = cm.emval_test_return_TupleVectorTuple(); + assert.deepEqual([[1, 2, 3]], c); + }); + + test("can pass tuples by value", function() { + var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6]); + assert.deepEqual([4, 5, 6], c); + }); + + test("can return structs by value", function() { + var c = cm.emval_test_return_StructVector(); + assert.deepEqual({x: 1, y: 2, z: 3}, c); + }); + + test("can pass structs by value", function() { + var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6}); + assert.deepEqual({x: 4, y: 5, z: 6}, c); + }); + + test("can pass and return tuples in structs", function() { + var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3]}); + assert.deepEqual({field: [1, 2, 3]}, d); + }); + + test("can clone handles", function() { + var a = new cm.ValHolder({}); + assert.equal(1, cm.count_emval_handles()); + var b = a.clone(); + a.delete(); + + assert.equal(1, cm.count_emval_handles()); + + assert.throws(cm.BindingError, function() { + a.delete(); + }); + b.delete(); + + assert.equal(0, cm.count_emval_handles()); + }); + + test("A shared pointer set/get point to the same underlying pointer", function() { + var a = new cm.SharedPtrHolder(); + var b = a.get(); + + a.set(b); + var c = a.get(); + + assert.equal(b.$$.ptr, c.$$.ptr); + b.delete(); + c.delete(); + a.delete(); + }); + + test("can return shared ptrs from instance methods", function() { + var a = new cm.SharedPtrHolder(); + + // returns the shared_ptr. + var b = a.get(); + + assert.equal("a string", b.get()); + b.delete(); + a.delete(); + }); + + test("smart ptrs clone correctly", function() { + assert.equal(0, cm.count_emval_handles()); + + var a = cm.emval_test_return_shared_ptr(); + + var b = a.clone(); + a.delete(); + + assert.equal(1, cm.count_emval_handles()); + + assert.throws(cm.BindingError, function() { + a.delete(); + }); + b.delete(); + + assert.equal(0, cm.count_emval_handles()); + }); + + test("can't clone if already deleted", function() { + var a = new cm.ValHolder({}); + a.delete(); + assert.throws(cm.BindingError, function() { + a.clone(); + }); + }); + + test("virtual calls work correctly", function() { + var derived = cm.embind_test_return_raw_polymorphic_derived_ptr_as_base(); + assert.equal("PolyDerived", derived.virtualGetClassName()); + derived.delete(); + }); + + test("virtual calls work correctly on smart ptrs", function() { + var derived = cm.embind_test_return_smart_polymorphic_derived_ptr_as_base(); + assert.equal("PolyDerived", derived.virtualGetClassName()); + derived.delete(); + }); + + test("Empty smart ptr is null", function() { + var a = cm.emval_test_return_empty_shared_ptr(); + assert.equal(null, a); + }); + + test("string cannot be given as smart pointer argument", function() { + assert.throws(cm.BindingError, function() { + cm.emval_test_is_shared_ptr_null("hello world"); + }); + }); + + test("number cannot be given as smart pointer argument", function() { + assert.throws(cm.BindingError, function() { + cm.emval_test_is_shared_ptr_null(105); + }); + }); + + test("raw pointer cannot be given as smart pointer argument", function() { + var p = new cm.ValHolder({}); + assert.throws(cm.BindingError, function() { cm.emval_test_is_shared_ptr_null(p); }); + p.delete(); + }); + + test("null is passed as empty smart pointer", function() { + assert.true(cm.emval_test_is_shared_ptr_null(null)); + }); + + test("Deleting already deleted smart ptrs fails", function() { + var a = cm.emval_test_return_shared_ptr(); + a.delete(); + assert.throws(cm.BindingError, function() { + a.delete(); + }); + }); + + test("StringHolder", function() { + var a = new cm.StringHolder("foobar"); + assert.equal("foobar", a.get()); + + a.set("barfoo"); + assert.equal("barfoo", a.get()); + + assert.equal("barfoo", a.get_const_ref()); + + a.delete(); + }); + + test("can call methods on unique ptr", function() { + var result = cm.emval_test_return_unique_ptr(); + + result.setVal('1234'); + assert.equal('1234', result.getVal()); + result.delete(); + }); + + test("can call methods on shared ptr", function(){ + var result = cm.emval_test_return_shared_ptr(); + result.setVal('1234'); + + assert.equal('1234', result.getVal()); + result.delete(); + }); + + test("Non functors throw exception", function() { + var a = {foo: 'bar'}; + var c = new cm.ValHolder(a); + assert.throws(TypeError, function() { + c(); + }); + c.delete(); + }); + + test("non-member methods", function() { + var a = {foo: 'bar'}; + var c = new cm.ValHolder(a); + c.setEmpty(); // non-member method + assert.deepEqual({}, c.getValNonMember()); + c.delete(); + }); + + test("instantiating class without constructor gives error", function() { + var e = assert.throws(cm.BindingError, function() { + cm.AbstractClass(); + }); + assert.equal("Use 'new' to construct AbstractClass", e.message); + + var e = assert.throws(cm.BindingError, function() { + new cm.AbstractClass(); + }); + assert.equal("AbstractClass has no accessible constructor", e.message); + }); + + test("can construct class with external constructor", function() { + var e = new cm.HasExternalConstructor("foo"); + assert.instanceof(e, cm.HasExternalConstructor); + assert.equal("foo", e.getString()); + e.delete(); + }); + }); + + BaseFixture.extend("const", function() { + test("calling non-const method with const handle is error", function() { + var vh = cm.ValHolder.makeConst({}); + var e = assert.throws(cm.BindingError, function() { + vh.setVal({}); + }); + assert.equal('Cannot convert argument of type ValHolder const* to parameter type ValHolder*', e.message); + vh.delete(); + }); + + test("passing const pointer to non-const pointer is error", function() { + var vh = new cm.ValHolder.makeConst({}); + var e = assert.throws(cm.BindingError, function() { + cm.ValHolder.set_via_raw_pointer(vh, {}); + }); + assert.equal('Cannot convert argument of type ValHolder const* to parameter type ValHolder*', e.message); + vh.delete(); + }); + }); + + BaseFixture.extend("smart pointers", function() { + test("constructor can return smart pointer", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.instanceof(e, cm.HeldBySmartPtr); + assert.equal(10, e.i); + assert.equal("foo", e.s); + var f = cm.takesHeldBySmartPtr(e); + f.delete(); + e.delete(); + }); + + test("cannot pass incorrect smart pointer type", function() { + var e = cm.emval_test_return_shared_ptr(); + assert.throws(cm.BindingError, function() { + cm.takesHeldBySmartPtr(e); + }); + e.delete(); + }); + + test("smart pointer object has no object keys", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.deepEqual([], Object.keys(e)); + + var f = e.clone(); + e.delete(); + + assert.deepEqual([], Object.keys(f)); + f.delete(); + }); + + test("smart pointer object has correct constructor name", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.equal('HeldBySmartPtr', e.constructor.name); + e.delete(); + }); + + test("constructor can return smart pointer", function() { + var e = new cm.HeldBySmartPtr(10, "foo"); + assert.instanceof(e, cm.HeldBySmartPtr); + assert.equal(10, e.i); + assert.equal("foo", e.s); + var f = cm.takesHeldBySmartPtr(e); + assert.instanceof(f, cm.HeldBySmartPtr); + f.delete(); + e.delete(); + }); + + test("custom smart pointer", function() { + var e = new cm.HeldByCustomSmartPtr(20, "bar"); + assert.instanceof(e, cm.HeldByCustomSmartPtr); + assert.equal(20, e.i); + assert.equal("bar", e.s); + e.delete(); + }); + + test("custom smart pointer passed through wiretype", function() { + var e = new cm.HeldByCustomSmartPtr(20, "bar"); + var f = cm.passThroughCustomSmartPtr(e); + e.delete(); + + assert.instanceof(f, cm.HeldByCustomSmartPtr); + assert.equal(20, f.i); + assert.equal("bar", f.s); + + f.delete(); + }); + + test("cannot give null to by-value argument", function() { + var e = assert.throws(cm.BindingError, function() { + cm.takesHeldBySmartPtr(null); + }); + assert.equal('null is not a valid HeldBySmartPtr', e.message); + }); + + test("raw pointer can take and give null", function() { + assert.equal(null, cm.passThroughRawPtr(null)); + }); + + test("custom smart pointer can take and give null", function() { + assert.equal(null, cm.passThroughCustomSmartPtr(null)); + }); + + test("cannot pass shared_ptr to CustomSmartPtr", function() { + var o = cm.HeldByCustomSmartPtr.createSharedPtr(10, "foo"); + var e = assert.throws(cm.BindingError, function() { + cm.passThroughCustomSmartPtr(o); + }); + assert.equal('Cannot convert argument of type NSt3__110shared_ptrI20HeldByCustomSmartPtrEE to parameter type 14CustomSmartPtrI20HeldByCustomSmartPtrE', e.message); + o.delete(); + }); + + test("custom smart pointers can be passed to shared_ptr parameter", function() { + var e = cm.HeldBySmartPtr.newCustomPtr(10, "abc"); + assert.equal(10, e.i); + assert.equal("abc", e.s); + + cm.takesHeldBySmartPtrSharedPtr(e).delete(); + e.delete(); + }); + + test("can call non-member functions as methods", function() { + var e = new cm.HeldBySmartPtr(20, "bar"); + var f = e.returnThis(); + e.delete(); + assert.equal(20, f.i); + assert.equal("bar", f.s); + f.delete(); + }); + }); + + BaseFixture.extend("enumerations", function() { + test("can compare enumeration values", function() { + assert.equal(cm.Enum.ONE, cm.Enum.ONE); + assert.notEqual(cm.Enum.ONE, cm.Enum.TWO); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + test("repr includes enum value", function() { + assert.equal('<#Enum_ONE {}>', IMVU.repr(cm.Enum.ONE)); + assert.equal('<#Enum_TWO {}>', IMVU.repr(cm.Enum.TWO)); + }); + } + + test("instanceof", function() { + assert.instanceof(cm.Enum.ONE, cm.Enum); + }); + + test("can pass and return enumeration values to functions", function() { + assert.equal(cm.Enum.TWO, cm.emval_test_take_and_return_Enum(cm.Enum.TWO)); + }); + }); + + BaseFixture.extend("C++11 enum class", function() { + test("can compare enumeration values", function() { + assert.equal(cm.EnumClass.ONE, cm.EnumClass.ONE); + assert.notEqual(cm.EnumClass.ONE, cm.EnumClass.TWO); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + test("repr includes enum value", function() { + assert.equal('<#EnumClass_ONE {}>', IMVU.repr(cm.EnumClass.ONE)); + assert.equal('<#EnumClass_TWO {}>', IMVU.repr(cm.EnumClass.TWO)); + }); + } + + test("instanceof", function() { + assert.instanceof(cm.EnumClass.ONE, cm.EnumClass); + }); + + test("can pass and return enumeration values to functions", function() { + assert.equal(cm.EnumClass.TWO, cm.emval_test_take_and_return_EnumClass(cm.EnumClass.TWO)); + }); + }); + + BaseFixture.extend("emval call tests", function() { + test("can call functions from C++", function() { + var called = false; + cm.emval_test_call_function(function(i, f, tv, sv) { + called = true; + assert.equal(10, i); + assert.equal(1.5, f); + assert.deepEqual([1.25, 2.5, 3.75], tv); + assert.deepEqual({x: 1.25, y: 2.5, z: 3.75}, sv); + }, 10, 1.5, [1.25, 2.5, 3.75], {x: 1.25, y: 2.5, z: 3.75}); + assert.true(called); + }); + }); + + BaseFixture.extend("extending built-in classes", function() { + // cm.ValHolder.prototype.patched = 10; // this sets instanceCounts.patched inside of Deletable module !?! + + test("can access patched value on new instances", function() { + cm.ValHolder.prototype.patched = 10; + var c = new cm.ValHolder(undefined); + assert.equal(10, c.patched); + c.delete(); + cm.ValHolder.prototype.patched = undefined; + }); + + test("can access patched value on returned instances", function() { + cm.ValHolder.prototype.patched = 10; + var c = cm.emval_test_return_ValHolder(); + assert.equal(10, c.patched); + c.delete(); + cm.ValHolder.prototype.patched = undefined; + }); + }); + + BaseFixture.extend("raw pointers", function() { + test("can pass raw pointers into functions if explicitly allowed", function() { + var vh = new cm.ValHolder({}); + cm.ValHolder.set_via_raw_pointer(vh, 10); + assert.equal(10, cm.ValHolder.get_via_raw_pointer(vh)); + vh.delete(); + }); + + test("can return raw pointers from functions if explicitly allowed", function() { + var p = cm.embind_test_return_raw_base_ptr(); + assert.equal("Base", p.getClassName()); + p.delete(); + }); + + test("can pass multiple raw pointers to functions", function() { + var target = new cm.ValHolder(undefined); + var source = new cm.ValHolder("hi"); + cm.ValHolder.transfer_via_raw_pointer(target, source); + assert.equal("hi", target.getVal()); + target.delete(); + source.delete(); + }); + }); + + BaseFixture.extend("JavaScript interface", function() { + this.setUp(function() { + this.testobj = { + "method1": function() { return 111; }, + "method2": function() { return 222; } + }; + }); + + test("pass js object to c++ and call its method", function() { + var obj = new cm.JSInterfaceHolder(this.testobj); + assert.equal(111, obj.callMethod("method1")); + assert.equal(222, obj.callMethod("method2")); + assert.equal(111, obj.callMethodUsingSharedPtr("method1")); + assert.equal(222, obj.callMethodUsingSharedPtr("method2")); + obj.delete(); + }); + }); + + BaseFixture.extend("abstract methods", function() { + test("can call abstract methods", function() { + var obj = cm.getAbstractClass(); + assert.equal("from concrete", obj.abstractMethod()); + obj.delete(); + }); + + test("can implement abstract methods in JavaScript", function() { + var expected = "my JS string"; + function MyImplementation() { + this.rv = expected; + } + MyImplementation.prototype.abstractMethod = function() { + return this.rv; + }; + + var impl = cm.AbstractClass.implement(new MyImplementation); + assert.equal(expected, impl.abstractMethod()); + assert.equal(expected, cm.callAbstractMethod(impl)); + impl.delete(); + }); + }); + + BaseFixture.extend("registration order", function() { + test("registration of tuple elements out of order leaves them in order", function() { + var ot = cm.getOrderedTuple(); + assert.instanceof(ot[0], cm.FirstElement); + assert.instanceof(ot[1], cm.SecondElement); + ot[0].delete(); + ot[1].delete(); + }); + + test("registration of struct elements out of order", function() { + var os = cm.getOrderedStruct(); + assert.instanceof(os.first, cm.FirstElement); + assert.instanceof(os.second, cm.SecondElement); + os.first.delete(); + os.second.delete(); + }); + }); + + if (typeof INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER === "undefined") { // TODO: Enable this to work in Emscripten runner as well! + + BaseFixture.extend("unbound types", function() { + function assertMessage(fn, message) { + var e = assert.throws(cm.UnboundTypeError, fn); + assert.equal(message, e.message); + } + + test("calling function with unbound types produces error", function() { + assertMessage( + function() { + cm.getUnboundClass(); + }, + 'Cannot call getUnboundClass due to unbound types: UnboundClass'); + }); + + test("unbound base class produces error", function() { + assertMessage( + function() { + cm.getHasUnboundBase(); + }, + 'Cannot call getHasUnboundBase due to unbound types: UnboundClass'); + }); + + test("construct of class with unbound base", function() { + assertMessage( + function() { + new cm.HasUnboundBase; + }, 'Cannot construct HasUnboundBase due to unbound types: UnboundClass'); + }); + + test("unbound constructor argument", function() { + assertMessage( + function() { + new cm.HasConstructorUsingUnboundArgument; + }, + 'Cannot construct HasConstructorUsingUnboundArgument due to unbound types: UnboundClass'); + }); + + test("unbound constructor argument of class with unbound base", function() { + assertMessage( + function() { + new cm.HasConstructorUsingUnboundArgumentAndUnboundBase; + }, + 'Cannot construct HasConstructorUsingUnboundArgumentAndUnboundBase due to unbound types: SecondUnboundClass'); + }); + + test('class function with unbound argument', function() { + var x = new cm.BoundClass; + assertMessage( + function() { + x.method(); + }, 'Cannot call BoundClass.method due to unbound types: UnboundClass'); + x.delete(); + }); + + test('class class function with unbound argument', function() { + assertMessage( + function() { + cm.BoundClass.classfunction(); + }, 'Cannot call BoundClass.classfunction due to unbound types: UnboundClass'); + }); + + test('class property of unbound type', function() { + var x = new cm.BoundClass; + var y; + assertMessage( + function() { + y = x.property; + }, 'Cannot access BoundClass.property due to unbound types: UnboundClass'); + assertMessage( + function() { + x.property = 10; + }, 'Cannot access BoundClass.property due to unbound types: UnboundClass'); + x.delete(); + }); + + // todo: tuple elements + // todo: tuple element accessors + // todo: struct fields + }); + + } + + BaseFixture.extend("noncopyable", function() { + test('can call method on noncopyable object', function() { + var x = new cm.Noncopyable; + assert.equal('foo', x.method()); + x.delete(); + }); + }); + + BaseFixture.extend("function names", function() { + assert.equal('ValHolder', cm.ValHolder.name); + assert.equal('ValHolder$setVal', cm.ValHolder.prototype.setVal.name); + assert.equal('ValHolder$makeConst', cm.ValHolder.makeConst.name); + }); +}); + +// If running as part of the emscripten test runner suite, and not as part of the IMVU suite, +// we launch the test execution from here. IMVU suite uses its own dedicated mechanism instead of this. +if (typeof run_all_tests !== "undefined") + run_all_tests(); diff --git a/tests/embind/embind_test.cpp b/tests/embind/embind_test.cpp index 485cdfcd..e9feccfe 100644 --- a/tests/embind/embind_test.cpp +++ b/tests/embind/embind_test.cpp @@ -1,5 +1,6 @@ #include <string>
#include <malloc.h>
+#include <functional>
#include <emscripten/bind.h>
using namespace emscripten;
@@ -27,6 +28,10 @@ val emval_test_new_string() { return val("Hello everyone");
}
+std::string emval_test_get_string_from_val(val v) {
+ return v["key"].as<std::string>();
+}
+
val emval_test_new_object() {
val rv(val::object());
rv.set("foo", val("bar"));
@@ -54,18 +59,22 @@ unsigned emval_test_as_unsigned(val v) { }
unsigned emval_test_get_length(val v) {
- return v.get("length").as<unsigned>();
+ return v["length"].as<unsigned>();
}
double emval_test_add(char c, signed char sc, unsigned char uc, signed short ss, unsigned short us, signed int si, unsigned int ui, signed long sl, unsigned long ul, float f, double d) {
return c + sc + uc + ss + us + si + ui + sl + ul + f + d;
}
+float const_ref_adder(const int& i, const float& f) {
+ return i + f;
+}
+
unsigned emval_test_sum(val v) {
- unsigned length = v.get("length").as<unsigned>();
+ unsigned length = v["length"].as<unsigned>();
double rv = 0;
for (unsigned i = 0; i < length; ++i) {
- rv += v.get(i).as<double>();
+ rv += v[i].as<double>();
}
return rv;
}
@@ -82,30 +91,70 @@ std::string emval_test_take_and_return_std_string_const_ref(const std::string& s return str;
}
+std::function<std::string (std::string)> emval_test_get_function_ptr() {
+ return emval_test_take_and_return_std_string;
+}
+
+std::string emval_test_take_and_call_functor(std::function<std::string(std::string)> func) {
+ return func("asdf");
+}
+
class ValHolder {
public:
ValHolder(val v)
- : v(v)
+ : v_(v)
{}
val getVal() const {
- return v;
+ return v_;
}
- void setVal(val v) {
- this->v = v;
+ val getValNonConst() {
+ return v_;
+ }
+
+ const val getConstVal() const {
+ return v_;
}
- int returnIntPlusFive( int x ) {
- return x + 5;
+ const val& getValConstRef() const {
+ return v_;
+ }
+
+ void setVal(val v) {
+ this->v_ = v;
}
static int some_class_method(int i) {
return i;
}
+ static const ValHolder* makeConst(val v) {
+ return new ValHolder(v);
+ }
+
+ static ValHolder makeValHolder(val v) {
+ return ValHolder(v);
+ }
+
+ static void set_via_raw_pointer(ValHolder* vh, val v) {
+ vh->setVal(v);
+ }
+
+ static val get_via_raw_pointer(const ValHolder* vh) {
+ return vh->getVal();
+ }
+
+ static void transfer_via_raw_pointer(ValHolder* target, const ValHolder* source) {
+ target->setVal(source->getVal());
+ }
+
+ static val getValNonMember(const ValHolder& target) {
+ return target.getVal();
+ }
+
private:
- val v;
+ val v_;
};
ValHolder emval_test_return_ValHolder() {
@@ -119,18 +168,556 @@ void emval_test_set_ValHolder_to_empty_object(ValHolder& vh) { class StringHolder {
public:
StringHolder(const std::string& s)
- : str(s)
+ : str_(s)
{}
void set(const std::string& s) {
- str = s;
+ str_ = s;
}
+
std::string get() const {
- return str;
+ return str_;
+ }
+
+ std::string& get_ref() {
+ return str_;
+ }
+
+ const std::string& get_const_ref() const {
+ return str_;
+ }
+
+private:
+ std::string str_;
+};
+
+class SharedPtrHolder {
+public:
+ SharedPtrHolder()
+ : ptr_(new StringHolder("a string"))
+ {}
+
+ std::shared_ptr<StringHolder> get() const {
+ return ptr_;
+ }
+
+ void set(std::shared_ptr<StringHolder> p) {
+ ptr_ = p;
+ }
+private:
+ std::shared_ptr<StringHolder> ptr_;
+};
+
+class VectorHolder {
+public:
+ VectorHolder() {
+ v_.push_back(StringHolder("string #1"));
+ v_.push_back(StringHolder("string #2"));
+ }
+
+ std::vector<StringHolder> get() const {
+ return v_;
+ }
+
+ void set(std::vector<StringHolder> vec) {
+ v_ = vec;
}
private:
- std::string str;
+ std::vector<StringHolder> v_;
+};
+
+class SmallClass {
+public:
+ SmallClass(): member(7) {};
+ int member;
+};
+
+class BigClass {
+public:
+ BigClass(): member(11) {};
+ int member;
+ int otherMember;
+ int yetAnotherMember;
+
+ int getMember() {
+ return member;
+ }
+};
+
+class ParentClass {
+public:
+ ParentClass(): bigClass() {};
+
+ BigClass bigClass;
+
+ const BigClass& getBigClass() {
+ return bigClass;
+ };
+};
+
+template<typename T>
+class TemplateClass {
+public:
+ TemplateClass(T a, T b, T c) {
+ members[0] = a;
+ members[1] = b;
+ members[2] = c;
+ };
+
+ const T getMember(int n) {
+ return members[n];
+ }
+
+protected:
+ T members[3];
+};
+
+class ContainsTemplatedMemberClass {
+public:
+ ContainsTemplatedMemberClass(): testTemplate(86, 87, 88) {};
+
+ TemplateClass<int> testTemplate;
+
+ const TemplateClass<int>& getTestTemplate() {
+ return testTemplate;
+ };
+};
+
+// Begin Inheritance Hierarchy Class Definitions
+
+class Base {
+public:
+ Base(): name("Base"),
+ member(0),
+ baseMember(0)
+ {}
+
+ std::string getClassName() const {
+ return name;
+ }
+ std::string getClassNameFromBase() const {
+ return name;
+ }
+ std::string getClassNameNotAvailableInDerivedClasses() {
+ // but wait -- if you act now we will throw in a SECOND base class method ABSOLUTELY FREE!!
+ return name;
+ }
+ void setMember(int value) {
+ member = value;
+ }
+ int getMember() {
+ return member;
+ }
+ void setBaseMember(int value) {
+ baseMember = value;
+ }
+ int getBaseMember() {
+ return baseMember;
+ }
+ std::string name;
+ int member;
+ int baseMember;
+};
+
+class SecondBase {
+public:
+ SecondBase()
+ : name("SecondBase"),
+ member(0),
+ secondBaseMember(0)
+ {}
+
+ std::string getClassName() const {
+ return name;
+ }
+ std::string getClassNameNotAvailableInDerivedClasses() {
+ return name;
+ }
+ std::string getClassNameFromSecondBase() const {
+ return name;
+ }
+ void setMember(int value) {
+ member = value;
+ }
+ int getMember() {
+ return member;
+ }
+ void setSecondBaseMember(int value) {
+ secondBaseMember = value;
+ }
+ int getSecondBaseMember() {
+ return secondBaseMember;
+ }
+ std::string name;
+ int member;
+ int secondBaseMember;
+};
+
+class Derived : public Base{
+public:
+ Derived()
+ : Base()
+ , member(0)
+ , name_("Derived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+ void setMember(int value) {
+ member = value;
+ }
+ int getMember() {
+ return member;
+ }
+ int member;
+private:
+ std::string name_;
+};
+
+class DerivedHolder {
+public:
+ DerivedHolder() {
+ derived_.reset();
+ }
+ void newDerived() {
+ deleteDerived();
+ derived_ = std::shared_ptr<Derived>(new Derived());
+ }
+ void deleteDerived() {
+ derived_.reset();
+ }
+ std::shared_ptr<Derived> getDerived() {
+ return derived_;
+ }
+ std::string getDerivedClassName() {
+ return derived_->getClassName();
+ }
+private:
+ std::shared_ptr<Derived> derived_;
+};
+
+class SiblingDerived : public Base {
+public:
+ SiblingDerived()
+ : Base(),
+ name_("SiblingDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+private:
+ std::string name_;
+};
+
+class MultiplyDerived : public Base, public SecondBase {
+public:
+ MultiplyDerived()
+ : Base(), SecondBase(),
+ name_("MultiplyDerived")
+ { instanceCount_ ++; }
+
+ ~MultiplyDerived()
+ { instanceCount_ --; }
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+ static int getInstanceCount() {
+ return instanceCount_;
+ }
+private:
+ std::string name_;
+ static int instanceCount_;
+};
+int MultiplyDerived::instanceCount_ = 0;
+
+class DerivedTwice : public Derived {
+public:
+ DerivedTwice()
+ : Derived(),
+ name_("DerivedTwice")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class DerivedTwiceNotBound : public Derived {
+public:
+ DerivedTwiceNotBound()
+ : Derived(),
+ name_("DerivedTwiceNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class DerivedThrice: public DerivedTwiceNotBound {
+public:
+ DerivedThrice()
+ : DerivedTwiceNotBound(),
+ name_("DerivedThrice")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class DerivedFourTimesNotBound: public DerivedThrice {
+public:
+ DerivedFourTimesNotBound()
+ : DerivedThrice(),
+ name_("DerivedFourTimesNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyBase {
+public:
+ PolyBase(const std::string& s)
+ : str_(s),
+ name_("PolyBase")
+ {}
+
+ PolyBase(): name_("PolyBase") {}
+
+ virtual ~PolyBase() {}
+
+ virtual std::string virtualGetClassName() const {
+ return name_;
+ }
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+private:
+ std::string str_;
+ std::string name_;
+};
+
+class PolySecondBase {
+public:
+ PolySecondBase(): name_("PolySecondBase")
+ {}
+
+ virtual ~PolySecondBase() {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerived : public PolyBase{
+public:
+ PolyDerived()
+ : PolyBase("PolyDerived"),
+ name_("PolyDerived")
+ {}
+
+ std::string virtualGetClassName() const {
+ return name_;
+ }
+
+ std::string getClassName() const {
+ return name_;
+ }
+
+ static void setPtrDerived() {
+ ptr_ = std::shared_ptr<PolyDerived>(new PolyDerived());
+ }
+
+ static void releasePtr() {
+ ptr_.reset();
+ }
+
+ static std::string getPtrClassName() {
+ return ptr_->getClassName();
+ }
+
+ static std::shared_ptr<PolyBase> getPtr() {
+ return ptr_;
+ }
+
+private:
+ std::string name_;
+ static std::shared_ptr<PolyBase> ptr_;
+};
+std::shared_ptr<PolyBase> PolyDerived::ptr_;
+
+class PolySiblingDerived : public PolyBase {
+public:
+ PolySiblingDerived()
+ : PolyBase(),
+ name_("PolySiblingDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyMultiplyDerived : public PolyBase, public PolySecondBase {
+public:
+ PolyMultiplyDerived()
+ : PolyBase(), PolySecondBase(),
+ name_("PolyMultiplyDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedTwiceWithoutSmartPointer: public PolyDerived {
+public:
+ PolyDerivedTwiceWithoutSmartPointer()
+ : PolyDerived(),
+ name_("PolyDerivedTwiceWithoutSmartPointer")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedTwiceNotBound : public PolyDerived {
+public:
+ PolyDerivedTwiceNotBound()
+ : PolyDerived(),
+ name_("PolyDerivedTwiceNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedThrice: public PolyDerivedTwiceNotBound {
+public:
+ PolyDerivedThrice()
+ : PolyDerivedTwiceNotBound(),
+ name_("PolyDerivedThrice")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDerivedFourTimesNotBound: public PolyDerivedThrice {
+public:
+ PolyDerivedFourTimesNotBound()
+ : PolyDerivedThrice(),
+ name_("PolyDerivedFourTimesNotBound")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+// todo: does it need to be polymorphic?
+// todo: virtual diamond pattern
+class PolyDiamondBase {
+public:
+ PolyDiamondBase():
+ name_("PolyBase")
+ {}
+ ~PolyDiamondBase() {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDiamondDerived: public PolyDiamondBase {
+public:
+ PolyDiamondDerived()
+ : PolyDiamondBase(),
+ name_("PolyDiamondDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDiamondSiblingDerived: public PolyDiamondBase {
+public:
+ PolyDiamondSiblingDerived()
+ : PolyDiamondBase(),
+ name_("PolyDiamondSiblingDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+class PolyDiamondMultiplyDerived: public PolyDiamondDerived, public PolyDiamondSiblingDerived {
+public:
+ PolyDiamondMultiplyDerived()
+ : PolyDiamondDerived(), PolyDiamondSiblingDerived(),
+ name_("PolyDiamondMultiplyDerived")
+ {}
+
+ std::string getClassName() const {
+ return name_;
+ }
+private:
+ std::string name_;
+};
+
+// End Inheritance Hierarchy Class Definitions
+
+std::map<std::string, int> embind_test_get_string_int_map() {
+ std::map<std::string, int> m;
+
+ m["one"] = 1;
+ m["two"] = 2;
+
+ return m;
};
struct TupleVector {
@@ -187,6 +774,11 @@ struct CustomStruct { CustomStruct()
: field(10)
{}
+
+ const int& getField() const {
+ return field;
+ }
+
int field;
};
@@ -210,52 +802,521 @@ EnumClass emval_test_take_and_return_EnumClass(EnumClass e) { return e;
}
-class Interface {
+void emval_test_call_function(val v, int i, float f, TupleVector tv, StructVector sv) {
+ v(i, f, tv, sv);
+}
+
+std::unique_ptr<ValHolder> emval_test_return_unique_ptr() {
+ return std::unique_ptr<ValHolder>(new ValHolder(val::object()));
+}
+
+std::shared_ptr<ValHolder> emval_test_return_shared_ptr() {
+ return std::shared_ptr<ValHolder>(new ValHolder(val::object()));
+}
+
+std::shared_ptr<ValHolder> emval_test_return_empty_shared_ptr() {
+ return std::shared_ptr<ValHolder>();
+}
+
+bool emval_test_is_shared_ptr_null(std::shared_ptr<ValHolder> p) {
+ return !p;
+}
+
+static SmallClass smallClass;
+static BigClass bigClass;
+
+SmallClass embind_test_return_small_class_instance() {
+ return smallClass;
+}
+
+BigClass embind_test_return_big_class_instance() {
+ return bigClass;
+}
+
+int embind_test_accept_small_class_instance(SmallClass c) {
+ return c.member;
+}
+
+int embind_test_accept_big_class_instance(BigClass c) {
+ return c.member;
+}
+
+// Begin Inheritance Hierarchy Test Wrappers
+
+Base* embind_test_return_raw_base_ptr() {
+ return new Base();
+}
+
+Base* embind_test_return_raw_derived_ptr_as_base() {
+ return new Derived();
+}
+
+Base* embind_test_return_raw_sibling_derived_ptr_as_base() {
+ return new SiblingDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_derived_ptr_as_base() {
+ return new PolyDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base() {
+ return new PolySiblingDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base() {
+ return new PolyMultiplyDerived();
+}
+
+PolySecondBase* embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base() {
+ return new PolyMultiplyDerived();
+}
+
+PolyBase* embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base() {
+ return new PolyDerivedFourTimesNotBound();
+}
+
+std::shared_ptr<Base> embind_test_return_smart_base_ptr() {
+ return std::shared_ptr<Base>(new Base());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_polymorphic_base_ptr() {
+ return std::shared_ptr<PolyBase>(new PolyBase("PolyBase"));
+}
+
+std::shared_ptr<Derived> embind_test_return_smart_derived_ptr() {
+ return std::shared_ptr<Derived>(new Derived());
+}
+
+std::shared_ptr<SiblingDerived> embind_test_return_smart_sibling_derived_ptr() {
+ return std::shared_ptr<SiblingDerived>(new SiblingDerived());
+}
+
+std::shared_ptr<MultiplyDerived> embind_test_return_smart_multiply_derived_ptr() {
+ return std::shared_ptr<MultiplyDerived>(new MultiplyDerived());
+}
+
+std::shared_ptr<DerivedThrice> embind_test_return_smart_derived_thrice_ptr() {
+ return std::shared_ptr<DerivedThrice>(new DerivedThrice());
+}
+
+std::shared_ptr<PolyDerived> embind_test_return_smart_polymorphic_derived_ptr() {
+ return std::shared_ptr<PolyDerived>(new PolyDerived());
+}
+
+std::shared_ptr<PolySiblingDerived> embind_test_return_smart_polymorphic_sibling_derived_ptr() {
+ return std::shared_ptr<PolySiblingDerived>(new PolySiblingDerived());
+}
+
+std::shared_ptr<PolyMultiplyDerived> embind_test_return_smart_polymorphic_multiply_derived_ptr() {
+ return std::shared_ptr<PolyMultiplyDerived>(new PolyMultiplyDerived());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base() {
+ return std::shared_ptr<PolyBase>(new PolyDerivedTwiceWithoutSmartPointer());
+}
+
+std::shared_ptr<PolyDerivedThrice> embind_test_return_smart_poly_derived_thrice_ptr() {
+ return std::shared_ptr<PolyDerivedThrice>(new PolyDerivedThrice());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_derived_ptr_as_base() {
+ return std::shared_ptr<PolyBase>(new PolyDerived());
+}
+
+val embind_test_return_smart_derived_ptr_as_val() {
+ return val(std::shared_ptr<PolyBase>(new PolyDerived()));
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_polymorphic_derived_ptr_as_base() {
+ return std::shared_ptr<PolyBase>(new PolyDerived());
+}
+
+std::shared_ptr<PolyBase> embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base() {
+ return std::shared_ptr<PolyBase>(new PolySiblingDerived());
+}
+
+std::string embind_test_get_class_name_via_base_ptr(Base *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_second_base_ptr(SecondBase *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_polymorphic_base_ptr(PolyBase *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_polymorphic_second_base_ptr(PolySecondBase *p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_base_ptr(std::shared_ptr<Base> p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_reference_to_smart_base_ptr(std::shared_ptr<Base>& p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_second_base_ptr(std::shared_ptr<SecondBase> p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_polymorphic_base_ptr(std::shared_ptr<PolyBase> p) {
+ return p->getClassName();
+}
+
+std::string embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr(std::shared_ptr<PolyBase> p) {
+ return p->virtualGetClassName();
+}
+
+std::string embind_test_get_class_name_via_smart_polymorphic_second_base_ptr(std::shared_ptr<PolySecondBase> p) {
+ return p->getClassName();
+}
+
+void embind_modify_smart_pointer_passed_by_reference(std::shared_ptr<Base>& p) {
+ p = std::shared_ptr<Base>(new Base());
+ p->name = "Changed";
+}
+
+void embind_attempt_to_modify_smart_pointer_when_passed_by_value(std::shared_ptr<Base> p) {
+ p = std::shared_ptr<Base>(new Base());
+ p->name = "Changed";
+}
+
+static std::shared_ptr<Base> savedBasePointer;
+
+void embind_save_smart_base_pointer(std::shared_ptr<Base> p) {
+ savedBasePointer = p;
+}
+
+// End Inheritance Hierarchy Test Wrappers
+
+std::vector<int> emval_test_return_vector() {
+ int myints[] = { 10, 20, 30 };
+ return std::vector<int>(myints, myints + sizeof(myints) / sizeof(int));
+}
+
+std::vector<std::vector<int> > emval_test_return_vector_of_vectors() {
+ int myints1[] = { 10, 20, 30 };
+ int myints2[] = { 40, 50, 60 };
+ std::vector<int> vec1(myints1, myints1 + sizeof(myints1) / sizeof(int));
+ std::vector<int> vec2(myints2, myints2 + sizeof(myints2) / sizeof(int));
+ std::vector<std::vector<int>>vec3;
+ vec3.emplace_back(vec1);
+ vec3.emplace_back(vec2);
+ return vec3;
+}
+
+std::vector<std::shared_ptr<StringHolder>> emval_test_return_shared_ptr_vector() {
+ std::vector<std::shared_ptr<StringHolder>> sharedStrVector;
+ sharedStrVector.push_back(std::shared_ptr<StringHolder>(new StringHolder("string #1")));
+ sharedStrVector.push_back(std::shared_ptr<StringHolder>(new StringHolder("string #2")));
+
+ return sharedStrVector;
+}
+
+class JSInterfaceHolder {
public:
- virtual int method() = 0;
- virtual TupleInStruct method2(const TupleInStruct& arg1, float arg2) = 0;
- virtual void method3() = 0;
+ JSInterfaceHolder(JSInterface &jsobj) : jsobj_(jsobj) {
+ ptr_ = JSInterface::cloneToSharedPtr(jsobj_);
+ }
+
+ int callMethod(std::string method) { return jsobj_.call<int>(method.c_str()); }
+ int callMethodUsingSharedPtr(std::string method) { return ptr_->call<int>(method.c_str()); }
+
+private:
+ JSInterface jsobj_;
+ std::shared_ptr<JSInterface> ptr_;
};
-int emval_test_call_method(Interface& i) {
- return i.method();
+void test_string_with_vec(const std::string& p1, std::vector<std::string>& v1) {
+ // THIS DOES NOT WORK -- need to get as val and then call vecFromJSArray
+ printf("%s\n", p1.c_str());
}
-TupleInStruct emval_test_call_method2(Interface& i, const TupleInStruct& arg1, float arg2) {
- return i.method2(arg1, arg2);
+val embind_test_new_Object() {
+ return val::global("Object").new_();
}
-void emval_test_call_method3(Interface& i) {
- i.method3();
+val embind_test_new_factory(val factory, val argument) {
+ return factory.new_(10, std::string("hello"), argument);
}
-void emval_test_call_function(val v, int i, float f, TupleVector tv, StructVector sv) {
- v(i, f, tv, sv);
+class AbstractClass {
+public:
+ virtual ~AbstractClass() {}
+ virtual std::string abstractMethod() const = 0;
+};
+
+class AbstractClassWrapper : public wrapper<AbstractClass> {
+public:
+ EMSCRIPTEN_WRAPPER(AbstractClassWrapper);
+
+ std::string abstractMethod() const {
+ return call<std::string>("abstractMethod");
+ }
+};
+
+class ConcreteClass : public AbstractClass {
+ std::string abstractMethod() const {
+ return "from concrete";
+ }
+};
+
+std::shared_ptr<AbstractClass> getAbstractClass() {
+ return std::make_shared<ConcreteClass>();
}
-void optional_test_copy() {
- using emscripten::internal::optional;
+std::string callAbstractMethod(AbstractClass& ac) {
+ return ac.abstractMethod();
+}
- optional<int> foo = 22;
- optional<int> bar(foo);
+class HasExternalConstructor {
+public:
+ HasExternalConstructor(const std::string& str)
+ : m(str)
+ {}
+
+ std::string getString() const {
+ return m;
+ }
+
+ std::string m;
+};
- return bool(bar);
+HasExternalConstructor* createHasExternalConstructor(const std::string& str) {
+ return new HasExternalConstructor(str);
}
-void optional_test_copy2() {
- using emscripten::internal::optional;
+template<typename T>
+class CustomSmartPtr {
+public:
+ CustomSmartPtr()
+ : CustomSmartPtr(nullptr)
+ {
+ std::fill(d_, d_ + N_, Valid);
+ }
+
+ explicit CustomSmartPtr(T* t)
+ : ptr_(t)
+ {
+ std::fill(d_, d_ + N_, Valid);
+ }
+
+ CustomSmartPtr(const CustomSmartPtr& other)
+ : ptr_(other.ptr_)
+ {
+ other.verify();
+ std::fill(d_, d_ + N_, Valid);
+ if (ptr_) {
+ ++(ptr_->refcount);
+ }
+ }
- optional<int> foo;
- optional<int> bar(foo);
+ ~CustomSmartPtr() {
+ verify();
+ std::fill(d_, d_ + N_, Deleted);
+
+ if (ptr_ && --ptr_->refcount == 0) {
+ delete ptr_;
+ }
+ }
- return bool(bar);
+ T* get_raw() const {
+ return ptr_;
+ }
+
+private:
+ void verify() const {
+ for (size_t i = 0; i < N_; ++i) {
+ if (d_[i] != Valid) {
+ abort();
+ }
+ }
+ }
+
+ enum {
+ Valid = 255,
+ Deleted = 127,
+ };
+ static constexpr size_t N_ = 1000000;
+ unsigned char d_[N_];
+ T* ptr_;
+
+ CustomSmartPtr& operator=(const CustomSmartPtr&) = delete;
+};
+
+class HeldBySmartPtr {
+public:
+ HeldBySmartPtr(int i, const std::string& s)
+ : i(i)
+ , s(s)
+ {}
+
+ static CustomSmartPtr<HeldBySmartPtr> newCustomPtr(int i, const std::string& s) {
+ return CustomSmartPtr<HeldBySmartPtr>(new HeldBySmartPtr(i, s));
+ }
+
+ int refcount = 1;
+ int i;
+ std::string s;
+};
+
+HeldBySmartPtr takesHeldBySmartPtr(HeldBySmartPtr p) {
+ return p;
+}
+std::shared_ptr<HeldBySmartPtr> takesHeldBySmartPtrSharedPtr(std::shared_ptr<HeldBySmartPtr> p) {
+ return p;
}
-EMSCRIPTEN_BINDINGS(([]() {
- function("mallinfo", &emval_test_mallinfo);
+namespace emscripten {
+ template<typename T>
+ struct smart_ptr_trait<CustomSmartPtr<T>> {
+ typedef T element_type;
+
+ static sharing_policy get_sharing_policy() {
+ return sharing_policy::NONE;
+ }
+
+ static T* get(const CustomSmartPtr<T>& p) {
+ return p.get_raw();
+ }
+
+ static CustomSmartPtr<T> share(const CustomSmartPtr<T>& r, T* ptr) {
+ ++ptr->refcount; // implement an adopt API?
+ return CustomSmartPtr<T>(ptr);
+ }
+ };
+}
+
+typedef CustomSmartPtr<class HeldByCustomSmartPtr> HeldByCustomSmartPtrPtr;
+
+class HeldByCustomSmartPtr {
+public:
+ HeldByCustomSmartPtr(int i, const std::string& s)
+ : i(i)
+ , s(s)
+ {}
+
+ static HeldByCustomSmartPtrPtr create(int i, const std::string& s) {
+ return HeldByCustomSmartPtrPtr(new HeldByCustomSmartPtr(i, s));
+ }
+ static std::shared_ptr<HeldByCustomSmartPtr> createSharedPtr(int i, const std::string& s) {
+ return std::make_shared<HeldByCustomSmartPtr>(i, s);
+ };
+
+ int refcount = 1;
+ int i;
+ std::string s;
+};
+
+HeldByCustomSmartPtr* passThroughRawPtr(HeldByCustomSmartPtr* p) {
+ return p;
+}
+HeldByCustomSmartPtrPtr passThroughCustomSmartPtr(HeldByCustomSmartPtrPtr p) {
+ return p;
+}
+
+struct Base1 {
+public:
+ Base1(): field1("Base1") {}
+ std::string field1;
+
+ std::string getField() const {
+ return field1;
+ }
+};
+
+struct Base2 {
+public:
+ Base2(): field2("Base2") {}
+ std::string field2;
+
+ std::string getField() const {
+ return field2;
+ }
+};
+
+struct HasTwoBases : public Base1, public Base2 {
+};
+
+val get_module_property(const std::string& s) {
+ return val::module_property(s.c_str());
+}
+
+std::string char_to_string(char ch) {
+ char str[256];
+ sprintf(str, "%d", (int)ch);
+ return str;
+}
+
+std::string signed_char_to_string(signed char ch) {
+ char str[256];
+ sprintf(str, "%hhd", ch);
+ return str;
+}
+
+std::string unsigned_char_to_string(unsigned char ch) {
+ char str[256];
+ sprintf(str, "%hhu", ch);
+ return str;
+}
+
+std::string short_to_string(short val) {
+ char str[256];
+ sprintf(str, "%hd", val);
+ return str;
+}
+
+std::string unsigned_short_to_string(unsigned short val) {
+ char str[256];
+ sprintf(str, "%hu", val);
+ return str;
+}
+
+std::string int_to_string(int val) {
+ char str[256];
+ sprintf(str, "%d", val);
+ return str;
+}
+
+std::string unsigned_int_to_string(unsigned int val) {
+ char str[256];
+ sprintf(str, "%u", val);
+ return str;
+}
+
+std::string long_to_string(long val) {
+ char str[256];
+ sprintf(str, "%ld", val);
+ return str;
+}
+
+std::string unsigned_long_to_string(unsigned long val) {
+ char str[256];
+ sprintf(str, "%lu", val);
+ return str;
+}
+
+EMSCRIPTEN_BINDINGS(tests) {
+ register_js_interface();
+
+ register_vector<int>("IntegerVector");
+ register_vector<char>("CharVector");
+ register_vector<unsigned>("VectorUnsigned");
+ register_vector<unsigned char>("VectorUnsignedChar");
+ register_vector<std::string>("StringVector");
+ register_vector<emscripten::val>("EmValVector");
+ register_vector<float>("FloatVector");
+ register_vector<std::vector<int>>("IntegerVectorVector");
+
+ function("mallinfo", &emval_test_mallinfo);
function("emval_test_new_integer", &emval_test_new_integer);
function("emval_test_new_string", &emval_test_new_string);
+ function("emval_test_get_string_from_val", &emval_test_get_string_from_val);
function("emval_test_new_object", &emval_test_new_object);
function("emval_test_passthrough_unsigned", &emval_test_passthrough_unsigned);
function("emval_test_passthrough", &emval_test_passthrough);
@@ -265,6 +1326,7 @@ EMSCRIPTEN_BINDINGS(([]() { function("emval_test_as_unsigned", &emval_test_as_unsigned);
function("emval_test_get_length", &emval_test_get_length);
function("emval_test_add", &emval_test_add);
+ function("const_ref_adder", &const_ref_adder);
function("emval_test_sum", &emval_test_sum);
//function("emval_test_take_and_return_const_char_star", &emval_test_take_and_return_const_char_star);
@@ -305,24 +1367,289 @@ EMSCRIPTEN_BINDINGS(([]() { function("emval_test_take_and_return_TupleInStruct", &emval_test_take_and_return_TupleInStruct);
class_<ValHolder>("ValHolder")
+ .smart_ptr<std::shared_ptr<ValHolder>>()
.constructor<val>()
- .method("getVal", &ValHolder::getVal)
- .method("setVal", &ValHolder::setVal)
- .method("returnIntPlusFive", &ValHolder::returnIntPlusFive)
- .classmethod("some_class_method", &ValHolder::some_class_method)
+ .function("getVal", &ValHolder::getVal)
+ .function("getValNonConst", &ValHolder::getValNonConst)
+ .function("getConstVal", &ValHolder::getConstVal)
+ .function("getValConstRef", &ValHolder::getValConstRef)
+ .function("setVal", &ValHolder::setVal)
+ .class_function("makeConst", &ValHolder::makeConst, allow_raw_pointer<ret_val>())
+ .class_function("makeValHolder", &ValHolder::makeValHolder)
+ .class_function("some_class_method", &ValHolder::some_class_method)
+ .class_function("set_via_raw_pointer",
+ &ValHolder::set_via_raw_pointer,
+ allow_raw_pointer<arg<0>>())
+ .class_function("get_via_raw_pointer",
+ &ValHolder::get_via_raw_pointer,
+ allow_raw_pointer<arg<0>>())
+ .class_function("transfer_via_raw_pointer",
+ &ValHolder::transfer_via_raw_pointer,
+ allow_raw_pointers())
+
+ // non-member method
+ .function("setEmpty", &emval_test_set_ValHolder_to_empty_object)
+ .function("getValNonMember", &ValHolder::getValNonMember)
;
+
function("emval_test_return_ValHolder", &emval_test_return_ValHolder);
function("emval_test_set_ValHolder_to_empty_object", &emval_test_set_ValHolder_to_empty_object);
+ class_<std::function<std::string(std::string)>>("StringFunctorString")
+ .constructor<>()
+ .calloperator<std::string, std::string>("opcall")
+ ;
+
+ function("emval_test_get_function_ptr", &emval_test_get_function_ptr);
+ function("emval_test_take_and_call_functor", &emval_test_take_and_call_functor);
+
class_<StringHolder>("StringHolder")
+ .smart_ptr<std::shared_ptr<StringHolder>>()
.constructor<std::string>()
- .method("set", &StringHolder::set)
- .method("get", &StringHolder::get)
+ .function("set", &StringHolder::set)
+ .function("get", &StringHolder::get)
+ .function("get_const_ref", &StringHolder::get_const_ref)
+ ;
+
+ class_<SharedPtrHolder>("SharedPtrHolder")
+ .constructor<>()
+ .function("get", &SharedPtrHolder::get)
+ .function("set", &SharedPtrHolder::set)
+ ;
+
+ class_<SmallClass>("SmallClass")
+ .constructor<>()
+ .property("member", &SmallClass::member)
+ ;
+
+ class_<BigClass>("BigClass")
+ .constructor<>()
+ .property("member", &BigClass::member)
+ .property("otherMember", &BigClass::otherMember)
+ .property("yetAnotherMember", &BigClass::yetAnotherMember)
+ .function("getMember", &BigClass::getMember)
+ ;
+
+ class_<ParentClass>("ParentClass")
+ .constructor<>()
+ .function("getBigClass", &ParentClass::getBigClass)
+ ;
+
+ class_<TemplateClass<int>>("IntTemplateClass")
+ .constructor<int, int, int>()
+ .function("getMember", &TemplateClass<int>::getMember)
+ ;
+
+ class_<ContainsTemplatedMemberClass>("ContainsTemplatedMemberClass")
+ .constructor<>()
+ .function("getTestTemplate", &ContainsTemplatedMemberClass::getTestTemplate)
+ ;
+
+ // register Derived before Base as a test that it's possible to
+ // register base classes afterwards
+ class_<Derived, base<Base>>("Derived")
+ .smart_ptr<std::shared_ptr<Derived>>()
+ .constructor<>()
+ .function("getClassName", &Derived::getClassName)
+ .function("getMember", &Derived::getMember)
+ .function("setMember", &Derived::setMember)
+ .property("member", &Derived::member)
+ ;
+
+ class_<Base>("Base")
+ .smart_ptr<std::shared_ptr<Base>>()
+ .constructor<>()
+ .function("getClassName", &Base::getClassName)
+ .function("getClassNameFromBase", &Base::getClassNameFromBase)
+ .function("getClassNameNotAvailableInDerivedClasses", &Base::getClassNameNotAvailableInDerivedClasses)
+ .function("getMember", &Base::getMember)
+ .function("setMember", &Base::setMember)
+ .function("getBaseMember", &Base::getBaseMember)
+ .function("setBaseMember", &Base::setBaseMember)
+ .property("member", &Base::member)
+ .property("baseMember", &Base::baseMember)
+ ;
+
+ class_<SecondBase>("SecondBase")
+ .smart_ptr<std::shared_ptr<SecondBase>>()
+ .constructor<>()
+ .function("getClassName", &SecondBase::getClassName)
+ .function("getClassNameFromSecondBase", &SecondBase::getClassNameFromSecondBase)
+ .function("getClassNameNotAvailableInDerivedClasses", &SecondBase::getClassNameNotAvailableInDerivedClasses)
+ .function("getMember", &SecondBase::getMember)
+ .function("setMember", &SecondBase::setMember)
+ .function("getSecondBaseMember", &SecondBase::getSecondBaseMember)
+ .function("setSecondBaseMember", &SecondBase::setSecondBaseMember)
+ .property("member", &SecondBase::member)
+ .property("secondBaseMember", &SecondBase::secondBaseMember)
+ ;
+
+
+ class_<DerivedHolder>("DerivedHolder")
+ .constructor<>()
+ .function("newDerived", &DerivedHolder::newDerived)
+ .function("deleteDerived", &DerivedHolder::deleteDerived)
+ .function("getDerived", &DerivedHolder::getDerived)
+ .function("getDerivedClassName", &DerivedHolder::getDerivedClassName)
+ ;
+
+ class_<SiblingDerived>("SiblingDerived")
+ .smart_ptr<std::shared_ptr<SiblingDerived>>()
+ .constructor<>()
+ .function("getClassName", &SiblingDerived::getClassName)
+ ;
+
+ class_<MultiplyDerived, base<Base>>("MultiplyDerived")
+ .smart_ptr<std::shared_ptr<MultiplyDerived>>()
+ .constructor<>()
+ .function("getClassName", &MultiplyDerived::getClassName)
+ .class_function("getInstanceCount", &MultiplyDerived::getInstanceCount)
+ ;
+
+ class_<DerivedTwice, base<Derived> >("DerivedTwice")
+ .constructor<>()
+ .function("getClassName", &DerivedTwice::getClassName)
+ ;
+
+ class_<DerivedThrice, base<Derived> >("DerivedThrice")
+ .smart_ptr<std::shared_ptr<DerivedThrice>>()
+ .constructor<>()
+ .function("getClassName", &DerivedThrice::getClassName)
+ ;
+
+ class_<PolyBase>("PolyBase")
+ .smart_ptr<std::shared_ptr<PolyBase>>()
+ .constructor<>()
+ .function("virtualGetClassName", &PolyBase::virtualGetClassName)
+ .function("getClassName", &PolyBase::getClassName)
+ ;
+
+ class_<PolySecondBase>("PolySecondBase")
+ .smart_ptr<std::shared_ptr<PolySecondBase>>()
+ .constructor<>()
+ .function("getClassName", &PolySecondBase::getClassName)
+ ;
+
+ class_<PolyDerived, base<PolyBase>>("PolyDerived")
+ .smart_ptr<std::shared_ptr<PolyDerived>>()
+ .constructor<>()
+ .function("virtualGetClassName", &PolyDerived::virtualGetClassName)
+ .function("getClassName", &PolyDerived::getClassName)
+ .class_function("setPtrDerived", &PolyDerived::setPtrDerived)
+ .class_function("releasePtr", &PolyDerived::releasePtr)
+ .class_function("getPtrClassName", &PolyDerived::getPtrClassName)
+ .class_function("getPtr", &PolyDerived::getPtr)
+ ;
+// static void setPtrDerived() {
+// ptr = std::shared_ptr<PolyDerived>(new PolyDerived());
+// }
+//
+// static std::string getPtrClassName() {
+// return ptr->getClassName();
+// }
+//
+// static std::shared_ptr<PolyBase> getPtr() {
+// return ptr;
+// }
+
+ class_<PolySiblingDerived, base<PolyBase>>("PolySiblingDerived")
+ .smart_ptr<std::shared_ptr<PolySiblingDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolySiblingDerived::getClassName)
+ ;
+
+ class_<PolyMultiplyDerived, base<PolyBase>>("PolyMultiplyDerived")
+ .smart_ptr<std::shared_ptr<PolyMultiplyDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyMultiplyDerived::getClassName)
+ ;
+
+ class_<PolyDerivedThrice, base<PolyDerived>>("PolyDerivedThrice")
+ .smart_ptr<std::shared_ptr<PolyDerivedThrice>>()
+ .constructor<>()
+ .function("getClassName", &PolyDerivedThrice::getClassName)
+ ;
+
+ class_<PolyDiamondBase>("PolyDiamondBase")
+ .smart_ptr<std::shared_ptr<PolyDiamondBase>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondBase::getClassName)
+ ;
+
+ class_<PolyDiamondDerived>("PolyDiamondDerived")
+ .smart_ptr<std::shared_ptr<PolyDiamondDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondDerived::getClassName)
+ ;
+
+ class_<PolyDiamondSiblingDerived>("PolyDiamondSiblingDerived")
+ .smart_ptr<std::shared_ptr<PolyDiamondSiblingDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondSiblingDerived::getClassName)
+ ;
+
+ class_<PolyDiamondMultiplyDerived>("PolyDiamondMultiplyDerived")
+ .smart_ptr<std::shared_ptr<PolyDiamondMultiplyDerived>>()
+ .constructor<>()
+ .function("getClassName", &PolyDiamondMultiplyDerived::getClassName)
+ ;
+
+ function("embind_test_return_small_class_instance", &embind_test_return_small_class_instance);
+ function("embind_test_return_big_class_instance", &embind_test_return_big_class_instance);
+ function("embind_test_accept_small_class_instance", &embind_test_accept_small_class_instance);
+ function("embind_test_accept_big_class_instance", &embind_test_accept_big_class_instance);
+
+ function("embind_test_return_raw_base_ptr", embind_test_return_raw_base_ptr, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_derived_ptr_as_base", embind_test_return_raw_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_sibling_derived_ptr_as_base", embind_test_return_raw_sibling_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_derived_ptr_as_base", embind_test_return_raw_polymorphic_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base", embind_test_return_raw_polymorphic_sibling_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base", embind_test_return_raw_polymorphic_multiply_derived_ptr_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base", embind_test_return_raw_polymorphic_multiply_derived_ptr_as_second_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base", embind_test_return_raw_polymorphic_derived_four_times_not_bound_as_base, allow_raw_pointer<ret_val>());
+ function("embind_test_return_smart_derived_ptr", embind_test_return_smart_derived_ptr);
+ function("embind_test_return_smart_sibling_derived_ptr", embind_test_return_smart_sibling_derived_ptr);
+ function("embind_test_return_smart_multiply_derived_ptr", embind_test_return_smart_multiply_derived_ptr);
+ function("embind_test_return_smart_derived_thrice_ptr", embind_test_return_smart_derived_thrice_ptr);
+ function("embind_test_return_smart_base_ptr", embind_test_return_smart_base_ptr);
+ function("embind_test_return_smart_polymorphic_base_ptr", embind_test_return_smart_polymorphic_base_ptr);
+ function("embind_test_return_smart_polymorphic_derived_ptr", embind_test_return_smart_polymorphic_derived_ptr);
+ function("embind_test_return_smart_polymorphic_sibling_derived_ptr", embind_test_return_smart_polymorphic_sibling_derived_ptr);
+ function("embind_test_return_smart_polymorphic_multiply_derived_ptr", embind_test_return_smart_polymorphic_multiply_derived_ptr);
+ function("embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base", embind_test_return_poly_derived_twice_without_smart_pointer_as_poly_base);
+ function("embind_test_return_smart_poly_derived_thrice_ptr", embind_test_return_smart_poly_derived_thrice_ptr);
+ function("embind_test_return_smart_derived_ptr_as_base", embind_test_return_smart_derived_ptr_as_base);
+ function("embind_test_return_smart_derived_ptr_as_val", embind_test_return_smart_derived_ptr_as_val);
+ function("embind_test_return_smart_polymorphic_derived_ptr_as_base", embind_test_return_smart_polymorphic_derived_ptr_as_base);
+ function("embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base", embind_test_return_smart_polymorphic_sibling_derived_ptr_as_base);
+ function("embind_test_get_class_name_via_base_ptr", embind_test_get_class_name_via_base_ptr, allow_raw_pointer<arg<0>>());
+ function("embind_test_get_class_name_via_second_base_ptr", embind_test_get_class_name_via_second_base_ptr, allow_raw_pointer<arg<0>>());
+ function("embind_test_get_class_name_via_polymorphic_base_ptr", embind_test_get_class_name_via_polymorphic_base_ptr, allow_raw_pointer<arg<0>>());
+ function("embind_test_get_class_name_via_polymorphic_second_base_ptr", embind_test_get_class_name_via_polymorphic_second_base_ptr, allow_raw_pointer<arg<0>>());
+ // todo: allow_raw_pointer should fail earlier if argument is not a pointer
+ function("embind_test_get_class_name_via_smart_base_ptr", embind_test_get_class_name_via_smart_base_ptr);
+ function("embind_test_get_class_name_via_reference_to_smart_base_ptr", embind_test_get_class_name_via_reference_to_smart_base_ptr);
+ function("embind_test_get_class_name_via_smart_second_base_ptr", embind_test_get_class_name_via_smart_second_base_ptr);
+ function("embind_test_get_class_name_via_smart_polymorphic_base_ptr", embind_test_get_class_name_via_smart_polymorphic_base_ptr);
+ function("embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr", embind_test_get_virtual_class_name_via_smart_polymorphic_base_ptr);
+ function("embind_test_get_class_name_via_smart_polymorphic_second_base_ptr", embind_test_get_class_name_via_smart_polymorphic_second_base_ptr);
+ function("embind_modify_smart_pointer_passed_by_reference", embind_modify_smart_pointer_passed_by_reference);
+ function("embind_attempt_to_modify_smart_pointer_when_passed_by_value", embind_attempt_to_modify_smart_pointer_when_passed_by_value);
+ function("embind_save_smart_base_pointer", embind_save_smart_base_pointer);
+
+ class_<Base2>("Base2")
+ .function("getField", &Base2::getField)
+ .property("field", &Base2::field2)
+ ;
+
+ class_<HasTwoBases, base<Base2>>("HasTwoBases")
+ .constructor()
;
class_<CustomStruct>("CustomStruct")
.constructor<>()
- .field("field", &CustomStruct::field)
+ .property("field", &CustomStruct::field)
+ .function("getField", &CustomStruct::getField)
;
enum_<Enum>("Enum")
@@ -337,25 +1664,221 @@ EMSCRIPTEN_BINDINGS(([]() { ;
function("emval_test_take_and_return_EnumClass", &emval_test_take_and_return_EnumClass);
- class InterfaceWrapper : public wrapper<Interface> {
- int method() {
- return call<int>("method");
- }
- TupleInStruct method2(const TupleInStruct& arg1, float arg2) {
- return call<TupleInStruct>("method2", arg1, arg2);
- }
- void method3() {
- return call<void>("method3");
- }
- };
- interface<InterfaceWrapper>("Interface")
+ function("emval_test_call_function", &emval_test_call_function);
+
+ function("emval_test_return_unique_ptr", &emval_test_return_unique_ptr);
+
+ function("emval_test_return_shared_ptr", &emval_test_return_shared_ptr);
+ function("emval_test_return_empty_shared_ptr", &emval_test_return_empty_shared_ptr);
+ function("emval_test_is_shared_ptr_null", &emval_test_is_shared_ptr_null);
+
+ function("emval_test_return_vector", &emval_test_return_vector);
+ function("emval_test_return_vector_of_vectors", &emval_test_return_vector_of_vectors);
+
+ register_vector<std::shared_ptr<StringHolder>>("SharedPtrVector");
+ function("emval_test_return_shared_ptr_vector", &emval_test_return_shared_ptr_vector);
+
+ function("get_module_property", &get_module_property);
+
+ register_vector<StringHolder>("StringHolderVector");
+ class_<VectorHolder>("VectorHolder")
+ .constructor<>()
+ .function("get", &VectorHolder::get)
+ .function("set", &VectorHolder::set)
;
- function("emval_test_call_method", &emval_test_call_method);
- function("emval_test_call_method2", &emval_test_call_method2);
- function("emval_test_call_method3", &emval_test_call_method3);
- function("emval_test_call_function", &emval_test_call_function);
+ function("test_string_with_vec", &test_string_with_vec);
+
+ register_map<std::string, int>("StringIntMap");
+ function("embind_test_get_string_int_map", embind_test_get_string_int_map);
+
+ class_<JSInterfaceHolder>("JSInterfaceHolder")
+ .constructor<JSInterface&>()
+ .function("callMethod", &JSInterfaceHolder::callMethod)
+ .function("callMethodUsingSharedPtr", &JSInterfaceHolder::callMethodUsingSharedPtr)
+ ;
+
+ function("embind_test_new_Object", &embind_test_new_Object);
+ function("embind_test_new_factory", &embind_test_new_factory);
+
+ class_<AbstractClass>("AbstractClass")
+ .smart_ptr<std::shared_ptr<AbstractClass>>()
+ .allow_subclass<AbstractClassWrapper>()
+ .function("abstractMethod", &AbstractClass::abstractMethod)
+ ;
+
+ function("getAbstractClass", &getAbstractClass);
+ function("callAbstractMethod", &callAbstractMethod);
+
+ class_<HasExternalConstructor>("HasExternalConstructor")
+ .constructor(&createHasExternalConstructor)
+ .function("getString", &HasExternalConstructor::getString)
+ ;
+
+ auto HeldBySmartPtr_class = class_<HeldBySmartPtr>("HeldBySmartPtr");
+ HeldBySmartPtr_class
+ .smart_ptr<CustomSmartPtr<HeldBySmartPtr>>()
+ .smart_ptr_constructor(&std::make_shared<HeldBySmartPtr, int, std::string>)
+ .class_function("newCustomPtr", HeldBySmartPtr::newCustomPtr)
+ .function("returnThis", &takesHeldBySmartPtrSharedPtr)
+ .property("i", &HeldBySmartPtr::i)
+ .property("s", &HeldBySmartPtr::s)
+ ;
+ function("takesHeldBySmartPtr", &takesHeldBySmartPtr);
+ function("takesHeldBySmartPtrSharedPtr", &takesHeldBySmartPtrSharedPtr);
+
+ class_<HeldByCustomSmartPtr>("HeldByCustomSmartPtr")
+ .smart_ptr<std::shared_ptr<HeldByCustomSmartPtr>>()
+ .smart_ptr_constructor(&HeldByCustomSmartPtr::create)
+ .class_function("createSharedPtr", &HeldByCustomSmartPtr::createSharedPtr)
+ .property("i", &HeldByCustomSmartPtr::i)
+ .property("s", &HeldByCustomSmartPtr::s)
+ ;
+
+ function("passThroughRawPtr", &passThroughRawPtr, allow_raw_pointers());
+ function("passThroughCustomSmartPtr", &passThroughCustomSmartPtr);
+
+ function("char_to_string", &char_to_string);
+ function("signed_char_to_string", &signed_char_to_string);
+ function("unsigned_char_to_string", &unsigned_char_to_string);
+ function("short_to_string", &short_to_string);
+ function("unsigned_short_to_string", &unsigned_short_to_string);
+ function("int_to_string", &int_to_string);
+ function("unsigned_int_to_string", &unsigned_int_to_string);
+ function("long_to_string", &long_to_string);
+ function("unsigned_long_to_string", &unsigned_long_to_string);
+}
+
+// tests for out-of-order registration
+
+class SecondElement {
+};
- function('optional_test_copy', &optional_test_copy);
- function('optional_test_copy2', &optional_test_copy2);
-}));
+class FirstElement {
+};
+
+struct OrderedTuple {
+ FirstElement first;
+ SecondElement second;
+};
+
+struct OrderedStruct {
+ FirstElement first;
+ SecondElement second;
+};
+
+OrderedTuple getOrderedTuple() {
+ return OrderedTuple();
+}
+
+OrderedStruct getOrderedStruct() {
+ return OrderedStruct();
+}
+
+EMSCRIPTEN_BINDINGS(order) {
+ value_tuple<OrderedTuple>("OrderedTuple")
+ .element(&OrderedTuple::first)
+ .element(&OrderedTuple::second)
+ ;
+
+ value_struct<OrderedStruct>("OrderedStruct")
+ .field("first", &OrderedStruct::first)
+ .field("second", &OrderedStruct::second)
+ ;
+
+ class_<SecondElement>("SecondElement")
+ ;
+
+ class_<FirstElement>("FirstElement")
+ ;
+
+ function("getOrderedTuple", &getOrderedTuple);
+ function("getOrderedStruct", &getOrderedStruct);
+}
+
+// tests for unbound types
+
+template<typename T>
+T passThrough(T t) {
+ return t;
+}
+
+struct UnboundClass {
+};
+
+struct HasUnboundBase : public UnboundClass {
+ static void noop() {
+ }
+};
+
+HasUnboundBase getHasUnboundBase(HasUnboundBase f) {
+ return f;
+}
+
+struct HasConstructorUsingUnboundArgument {
+ HasConstructorUsingUnboundArgument(UnboundClass) {
+ }
+};
+
+struct SecondUnboundClass {
+};
+
+struct HasConstructorUsingUnboundArgumentAndUnboundBase : public SecondUnboundClass {
+ HasConstructorUsingUnboundArgumentAndUnboundBase(UnboundClass) {
+ }
+};
+
+struct BoundClass {
+ UnboundClass method(UnboundClass t) {
+ return t;
+ }
+
+ static UnboundClass classfunction(UnboundClass t) {
+ return t;
+ }
+
+ UnboundClass property;
+};
+
+EMSCRIPTEN_BINDINGS(incomplete) {
+ function("getUnboundClass", &passThrough<UnboundClass>);
+
+ class_<HasUnboundBase, base<UnboundClass>>("HasUnboundBase")
+ .class_function("noop", &HasUnboundBase::noop)
+ ;
+ function("getHasUnboundBase", &passThrough<HasUnboundBase>);
+
+ class_<HasConstructorUsingUnboundArgument>("HasConstructorUsingUnboundArgument")
+ .constructor<UnboundClass>()
+ ;
+
+ class_<HasConstructorUsingUnboundArgumentAndUnboundBase, base<SecondUnboundClass>>("HasConstructorUsingUnboundArgumentAndUnboundBase")
+ .constructor<UnboundClass>()
+ ;
+
+ class_<BoundClass>("BoundClass")
+ .constructor<>()
+ .function("method", &BoundClass::method)
+ .class_function("classfunction", &BoundClass::classfunction)
+ .property("property", &BoundClass::property)
+ ;
+}
+
+class Noncopyable {
+ Noncopyable(const Noncopyable&) = delete;
+ Noncopyable& operator=(const Noncopyable&) = delete;
+
+public:
+ Noncopyable() {}
+
+ std::string method() const {
+ return "foo";
+ }
+};
+
+EMSCRIPTEN_BINDINGS(noncopyable) {
+ class_<Noncopyable>("Noncopyable")
+ .constructor<>()
+ .function("method", &Noncopyable::method)
+ ;
+}
diff --git a/tests/embind/embind_test.js b/tests/embind/embind_test.js deleted file mode 100644 index 9ba6a48f..00000000 --- a/tests/embind/embind_test.js +++ /dev/null @@ -1,398 +0,0 @@ -//=== testing glue - -function module(ignore, func) { - func({ Emscripten: Module }); -} - -function fixture(name, info) { - Module.print('fixture: ' + name); - for (var test in info) { - var f = info[test]; - if (typeof f != 'function') continue; - Module.print('--test: ' + test); - // TODO: Base fixture! - f(); - } -} - -assert.true = assert; - -assert.equal = function(x, y) { - assert(x == y); -} - -assert.notEqual = function(x, y) { - assert(x != y); -} - -assert.throws = function(exc, func) { - var ret; - try { - func(); - } catch(e) { - ret = e; - } - assert(ret); // TODO: check exc vs e - return ret; -} - -assert.instanceof = function(inst, clazz) { - assert(inst instanceof clazz); -} - -assert.deepEqual = function(x, y) { - assert(JSON.stringify(x) == JSON.stringify(y)); -} - -//=== - -module({ - Emscripten: '../build/Emscripten.js' -}, function(imports) { - var cm = imports.Emscripten; - - var checkForLeaks = { - setUp: function() { - this.originalBlockCount = cm.mallinfo().uordblks; - }, - tearDown: function() { - assert.equal(this.originalBlockCount, cm.mallinfo().uordblks); - }, - }; - - fixture("embind", { - baseFixture: checkForLeaks, - - "test value creation": function() { - assert.equal(15, cm.emval_test_new_integer()); - assert.equal("Hello everyone", cm.emval_test_new_string()); - - var object = cm.emval_test_new_object(); - assert.equal('bar', object.foo); - assert.equal(1, object.baz); - }, - - "test passthrough": function() { - var a = {foo: 'bar'}; - var b = cm.emval_test_passthrough(a); - a.bar = 'baz'; - assert.equal('baz', b.bar); - - assert.equal(0, cm.count_emval_handles()); - }, - - "test void return converts to undefined": function() { - assert.equal(undefined, cm.emval_test_return_void()); - }, - - "test booleans can be marshalled": function() { - assert.equal(false, cm.emval_test_not(true)); - assert.equal(true, cm.emval_test_not(false)); - }, - - "test convert double to unsigned": function() { - var rv = cm.emval_test_as_unsigned(1.5); - assert.equal('number', typeof rv); - assert.equal(1, rv); - assert.equal(0, cm.count_emval_handles()); - }, - - "test get length of array": function() { - assert.equal(10, cm.emval_test_get_length([0, 1, 2, 3, 4, 5, 'a', 'b', 'c', 'd'])); - assert.equal(0, cm.count_emval_handles()); - }, - - "test add a bunch of things": function() { - assert.equal(66.0, cm.emval_test_add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)); - assert.equal(0, cm.count_emval_handles()); - }, - - "test sum array": function() { - assert.equal(66, cm.emval_test_sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])); - assert.equal(0, cm.count_emval_handles()); - }, - - "test strings": function() { - assert.equal("foobar", "foo" + "bar"); - assert.equal("foobar", cm.emval_test_take_and_return_std_string("foobar")); - - assert.equal("foobar", cm.emval_test_take_and_return_std_string_const_ref("foobar")); - }, - - "test no memory leak when passing strings in by const reference": function() { - var original = cm.mallinfo().uordblks; - cm.emval_test_take_and_return_std_string_const_ref("foobar"); - assert.equal(original, cm.mallinfo().uordblks); - }, - }); - - fixture("classes", { - baseFixture: checkForLeaks, - - "test class instance": function() { - var a = {foo: 'bar'}; - var c = new cm.ValHolder(a); - assert.equal('bar', c.getVal().foo); - - c.setVal('1234'); - assert.equal('1234', c.getVal()); - - assert.equal(1239, c.returnIntPlusFive(1234)); - - c.delete(); - assert.equal(0, cm.count_emval_handles()); - }, - - "test class methods": function() { - assert.equal(10, cm.ValHolder.some_class_method(10)); - }, - - "test can't call methods on deleted class instances": function() { - var c = new cm.ValHolder(undefined); - c.delete(); - assert.throws(cm.BindingError, function() { - c.getVal(); - }); - assert.throws(cm.BindingError, function() { - c.delete(); - }); - }, - - "test isinstance": function() { - var c = new cm.ValHolder(undefined); - assert.instanceof(c, cm.ValHolder); - c.delete(); - }, - - "test can return class instances by value": function() { - var c = cm.emval_test_return_ValHolder(); - assert.deepEqual({}, c.getVal()); - c.delete(); - }, - - "test can pass class instances to functions by reference": function() { - var a = {a:1}; - var c = new cm.ValHolder(a); - cm.emval_test_set_ValHolder_to_empty_object(c); - assert.deepEqual({}, c.getVal()); - c.delete(); - }, - - "test can access struct fields": function() { - var c = new cm.CustomStruct(); - assert.equal(10, c.field); - c.delete(); - }, - - "test can set struct fields": function() { - var c = new cm.CustomStruct(); - c.field = 15; - assert.equal(15, c.field); - c.delete(); - }, - - "test assignment returns value": function() { - var c = new cm.CustomStruct(); - assert.equal(15, c.field = 15); - c.delete(); - }, - - "test assigning string to integer raises TypeError": function() { - var c = new cm.CustomStruct(); - - var e = assert.throws(TypeError, function() { - c.field = "hi"; - }); - assert.equal('Cannot convert "hi" to int', e.message); - - var e = assert.throws(TypeError, function() { - c.field = {foo:'bar'}; - }); - assert.equal('Cannot convert "[object Object]" to int', e.message); - - c.delete(); - }, - - "test can return tuples by value": function() { - var c = cm.emval_test_return_TupleVector(); - assert.deepEqual([1, 2, 3], c); - }, - - "test tuples can contain tuples": function() { - var c = cm.emval_test_return_TupleVectorTuple(); - assert.deepEqual([[1, 2, 3]], c); - }, - - "test can pass tuples by value": function() { - var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6]); - assert.deepEqual([4, 5, 6], c); - }, - - "test can return structs by value": function() { - var c = cm.emval_test_return_StructVector(); - assert.deepEqual({x: 1, y: 2, z: 3}, c); - }, - - "test can pass structs by value": function() { - var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6}); - assert.deepEqual({x: 4, y: 5, z: 6}, c); - }, - - "test can pass and return tuples in structs": function() { - var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3]}); - assert.deepEqual({field: [1, 2, 3]}, d); - }, - - "test can clone handles": function() { - assert.equal(0, cm.count_emval_handles()); - - var a = new cm.ValHolder({}); - var b = a.clone(); - a.delete(); - - assert.equal(1, cm.count_emval_handles()); - - assert.throws(cm.BindingError, function() { - a.delete(); - }); - b.delete(); - - assert.equal(0, cm.count_emval_handles()); - }, - - "test can't clone if already deleted": function() { - var a = new cm.ValHolder({}); - a.delete(); - assert.throws(cm.BindingError, function() { - a.clone(); - }); - }, - - "test moving handles is a clone+delete": function() { - var a = new cm.ValHolder({}); - var b = a.move(); - assert.throws(cm.BindingError, function() { - a.delete(); - }); - assert.equal(1, cm.count_emval_handles()); - b.delete(); - assert.equal(0, cm.count_emval_handles()); - }, - - "test StringHolder": function() { - var a = new cm.StringHolder("foobar"); - assert.equal("foobar", a.get()); - - a.set("barfoo"); - assert.equal("barfoo", a.get()); - a.delete(); - }, - }); - - fixture("embind enumerations", { - baseFixture: checkForLeaks, - - "test can compare enumeration values": function() { - assert.equal(cm.Enum.ONE, cm.Enum.ONE); - assert.notEqual(cm.Enum.ONE, cm.Enum.TWO); - }, - - "test repr includes enum value": function() { - return; // XXX IMVU? - assert.equal('<#Enum_ONE {}>', IMVU.repr(cm.Enum.ONE)); - assert.equal('<#Enum_TWO {}>', IMVU.repr(cm.Enum.TWO)); - }, - - "test instanceof": function() { - assert.instanceof(cm.Enum.ONE, cm.Enum); - }, - - "test can pass and return enumeration values to functions": function() { - assert.equal(cm.Enum.TWO, cm.emval_test_take_and_return_Enum(cm.Enum.TWO)); - }, - }); - - fixture("C++11 enum class", { - baseFixture: checkForLeaks, - - "test can compare enumeration values": function() { - assert.equal(cm.EnumClass.ONE, cm.EnumClass.ONE); - assert.notEqual(cm.EnumClass.ONE, cm.EnumClass.TWO); - }, - - "test repr includes enum value": function() { - return; // XXX IMVU? - assert.equal('<#EnumClass_ONE {}>', IMVU.repr(cm.EnumClass.ONE)); - assert.equal('<#EnumClass_TWO {}>', IMVU.repr(cm.EnumClass.TWO)); - }, - - "test instanceof": function() { - assert.instanceof(cm.EnumClass.ONE, cm.EnumClass); - }, - - "test can pass and return enumeration values to functions": function() { - assert.equal(cm.EnumClass.TWO, cm.emval_test_take_and_return_EnumClass(cm.EnumClass.TWO)); - }, - }); - - fixture("emval call tests", { - "test can call functions from C++": function() { - var called = false; - cm.emval_test_call_function(function(i, f, tv, sv) { - called = true; - assert.equal(10, i); - assert.equal(1.5, f); - assert.deepEqual([1.25, 2.5, 3.75], tv); - assert.deepEqual({x: 1.25, y: 2.5, z: 3.75}, sv); - }, 10, 1.5, [1.25, 2.5, 3.75], {x: 1.25, y: 2.5, z: 3.75}); - assert.true(called); - }, - }); - - fixture("interfaces", { - baseFixture: checkForLeaks, - - "test can wrap JS object in native interface": function() { - var foo = { - calls: [], - method: function() { - this.calls.push('called'); - return 10; - } - }; - - assert.equal(10, cm.emval_test_call_method(foo)); - assert.deepEqual(['called'], foo.calls); - }, - - "test can pass arguments and return complicated values": function() { - var calls = []; - var foo = { - method2: function(arg1, arg2) { - calls.push([arg1, arg2]); - return arg1; - } - }; - - var result = cm.emval_test_call_method2(foo, {field: [1, 2, 3]}, 7); - assert.deepEqual({field: [1, 2, 3]}, result); - assert.deepEqual([[{field: [1, 2, 3]}, 7]], calls); - }, - - "test can call interface methods that return nothing": function() { - var calls = []; - var foo = { - method3: function() { - calls.push('called'); - } - }; - cm.emval_test_call_method3(foo); - assert.deepEqual(['called'], calls); - }, - }); - - test('emscripten::internal::optional', function () { - assert.true(cm.optional_test_copy()); - assert.false(cm.optional_test_copy2()); - }); -}); diff --git a/tests/embind/imvu_test_adapter.js b/tests/embind/imvu_test_adapter.js new file mode 100755 index 00000000..03405d08 --- /dev/null +++ b/tests/embind/imvu_test_adapter.js @@ -0,0 +1,614 @@ +/* The embind test suite (embind.test.js) is configured to be runnable in two different testing engines:
+ - The Emscripten python test runner (open-source in emscripten repository) and
+ - The IMVU test runner (closed-source in IMVU repository)
+
+ Embind (and its tests) were originally developed in IMVU repository, which is the reason for two testing architectures.
+ This adapter file is used when the embind tests are run as part of the Emscripten test runner, to provide the necessary glue code to adapt the tests to Emscripten runner.
+
+ To run the Embind tests using the Emscripten test runner, invoke 'python tests/runner.py other.test_embind' in the Emscripten root directory.
+*/
+
+//=== testing glue
+
+function module(ignore, func) {
+ func({ Emscripten: Module });
+}
+
+/*global IMVU:true, TEST_MAX_OUTPUT_SIZE*/
+//(function() {
+// "use strict";
+
+ // { beforeTest: function,
+ // afterTest: function }
+ var superFixtures = [];
+
+ function registerSuperFixture(superFixture) {
+ superFixtures.push(superFixture);
+ }
+
+ // { fixture: Fixture instance,
+ // name: string,
+ // body: function() }
+ var allTests = [];
+
+ function test(name, fn) {
+ if (arguments.length !== 2) {
+ throw new TypeError("test requires 2 arguments");
+ }
+
+ if (undefined !== activeFixture && activeFixture.abstract) {
+ activeFixture.abstractTests.push({
+ name: name,
+ body: fn });
+ } else {
+ var fixtureName = (undefined !== activeFixture)? activeFixture.name + ': ' : '';
+ allTests.push({
+ name: fixtureName + name,
+ body: fn,
+ fixture: activeFixture });
+ }
+ }
+
+ function runTest(test, continuation) {
+ try {
+ var afterTests = [];
+
+ for (var i = 0; i < superFixtures.length; ++i) {
+ var superFixture = superFixtures[i];
+
+ var superScope = {};
+ superFixture.beforeTest.call(superScope);
+ afterTests.push(superFixture.afterTest.bind(superScope));
+ }
+
+ var testScope = test.fixture ?
+ Object.create(test.fixture.scope) :
+ {};
+
+ var runSetUp = function(fixtureObject) {
+ if (undefined === fixtureObject) {
+ return;
+ }
+ runSetUp(fixtureObject.parent);
+ fixtureObject.setUp.call(testScope);
+ afterTests.push(fixtureObject.tearDown.bind(testScope));
+ };
+ runSetUp(test.fixture);
+
+ test.body.call(testScope);
+ while (afterTests.length) {
+ afterTests.pop()();
+ }
+ return false;
+ } catch (e) {
+ console.error('error:', e);
+ return {stack: e.stack, e: e};
+ }
+ }
+
+ function run_all(reporter) {
+ for (var i = 0; i < allTests.length; ++i) {
+ var test = allTests[i];
+ reporter({
+ type: 'test-start',
+ name: test.name
+ });
+
+ var failed = runTest(test);
+ if (failed) {
+ reporter({
+ type: 'test-complete',
+ name: test.name,
+ verdict: 'FAIL',
+ stack: failed.stack,
+ e: failed.e
+ });
+ return false;
+ } else {
+ reporter({
+ type: 'test-complete',
+ name: test.name,
+ verdict: 'PASS'
+ });
+ }
+ }
+
+ reporter({
+ type: 'all-tests-complete'
+ });
+
+ allTests = [];
+ return true;
+ }
+
+ var activeFixture;
+
+ function Fixture(parent, name, definition, abstract_) {
+ if (!(definition instanceof Function)) {
+ throw new TypeError("fixture's 2nd argument must be a function");
+ }
+
+ this.name = name;
+ this.parent = parent;
+ this.abstract = abstract_;
+ if (this.abstract) {
+ // { name: string,
+ // body: function }
+ this.abstractTests = [];
+ }
+
+ if (this.parent !== undefined) {
+ this.parent.addAbstractTests(this);
+ }
+
+ this.scope = (this.parent === undefined ? {} : Object.create(this.parent.scope));
+ this.scope.setUp = function(setUp) {
+ this.setUp = setUp;
+ }.bind(this);
+ this.scope.tearDown = function(tearDown) {
+ this.tearDown = tearDown;
+ }.bind(this);
+
+ if (undefined !== activeFixture) {
+ throw new TypeError("Cannot define a fixture within another fixture");
+ }
+
+ activeFixture = this;
+ try {
+ definition.call(this.scope);
+ }
+ finally {
+ activeFixture = undefined;
+ }
+ }
+ Fixture.prototype.setUp = function defaultSetUp() {
+ };
+ Fixture.prototype.tearDown = function defaultTearDown() {
+ };
+ Fixture.prototype.addAbstractTests = function(concreteFixture) {
+ if (this.abstract) {
+ for (var i = 0; i < this.abstractTests.length; ++i) {
+ var test = this.abstractTests[i];
+ allTests.push({
+ name: concreteFixture.name + ': ' + test.name,
+ body: test.body,
+ fixture: concreteFixture});
+ }
+ }
+ if (this.parent) {
+ this.parent.addAbstractTests(concreteFixture);
+ }
+ };
+
+ Fixture.prototype.extend = function(fixtureName, definition) {
+ return new Fixture(this, fixtureName, definition, false);
+ };
+
+ function fixture(fixtureName, definition) {
+ return new Fixture(undefined, fixtureName, definition, false);
+ }
+ fixture.abstract = function(fixtureName, definition) {
+ return new Fixture(undefined, fixtureName, definition, true);
+ };
+
+ var AssertionError = Error;
+
+ function fail(exception, info) {
+ exception.info = info;
+ throw exception;
+ }
+
+ var formatTestValue = function(v) {
+ return v.toString();
+ /*
+ var s = IMVU.repr(v, TEST_MAX_OUTPUT_SIZE + 1);
+ if (s.length <= TEST_MAX_OUTPUT_SIZE) {
+ return s;
+ }
+ return s.substring(0, TEST_MAX_OUTPUT_SIZE) + '...';
+ */
+ };
+
+// var assert = {
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // GENERAL STATUS
+
+ assert.fail = function(info) {
+ info = info || "assert.fail()";
+ fail(new AssertionError(info));
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // BOOLEAN TESTS
+
+ assert['true'] = function(value) {
+ if (!value) {
+ fail(new AssertionError("expected truthy, actual " + formatTestValue(value)),
+ {Value: value});
+ }
+ },
+
+ assert['false'] = function(value) {
+ if (value) {
+ fail(new AssertionError("expected falsy, actual " + formatTestValue(value)),
+ {Value: value});
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // SCALAR COMPARISON
+
+ assert.equal = function(expected, actual) {
+ if (expected !== actual) {
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual)),
+ {Expected: expected, Actual: actual});
+ }
+ },
+
+ assert.notEqual = function(expected, actual) {
+ if (expected === actual) {
+ fail(new AssertionError('actual was equal to: ' + formatTestValue(expected)));
+ }
+ },
+
+ assert.greater = function(lhs, rhs) {
+ if (lhs <= rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not greater than ' + formatTestValue(rhs)));
+ }
+ },
+
+ assert.less = function(lhs, rhs) {
+ if (lhs >= rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not less than ' + formatTestValue(rhs)));
+ }
+ },
+
+ assert.greaterOrEqual = function(lhs, rhs) {
+ if (lhs < rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not greater than or equal to ' + formatTestValue(rhs)));
+ }
+ },
+
+ assert.lessOrEqual = function(lhs, rhs) {
+ if (lhs > rhs) {
+ fail(new AssertionError(formatTestValue(lhs) + ' not less than or equal to ' + formatTestValue(rhs)));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // DEEP COMPARISON
+
+ assert.deepEqual = function(expected, actual) {
+ if (!_.isEqual(expected, actual)) {
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual)),
+ {Expected: expected, Actual: actual});
+ }
+ },
+
+ assert.notDeepEqual = function(expected, actual) {
+ if (_.isEqual(expected, actual)) {
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ' and actual: ' + formatTestValue(actual) + ' were equal'));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // FLOATING POINT
+
+ assert.nearEqual = function( expected, actual, tolerance ) {
+ if( tolerance === undefined ) {
+ tolerance = 0.0;
+ }
+ if( expected instanceof Array && actual instanceof Array ) {
+ assert.equal(expected.length, actual.length);
+ for( var i=0; i<expected.length; ++i ) {
+ assert.nearEqual(expected[i], actual[i], tolerance);
+ }
+ return;
+ }
+ if( Math.abs(expected - actual) > tolerance ) {
+ fail( new AssertionError('expected: ' + formatTestValue(expected) + ', actual: ' + formatTestValue(actual) +
+ ', tolerance: ' + formatTestValue(tolerance) + ', diff: ' + formatTestValue(actual-expected) ),
+ { Expected:expected, Actual:actual, Tolerance:tolerance } );
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // STRING
+
+ assert.inString = function(expected, string){
+ if (-1 === string.indexOf(expected)){
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ' not in string: ' + formatTestValue(string)),
+ {Expected: expected, 'String': string});
+ }
+ },
+
+ assert.notInString = function(expected, string){
+ if (-1 !== string.indexOf(expected)){
+ fail(new AssertionError('unexpected: ' + formatTestValue(expected) + ' in string: ' + formatTestValue(string)),
+ {Expected: expected, 'String': string});
+ }
+ },
+
+ assert.matches = function(re, string) {
+ if (!re.test(string)) {
+ fail(new AssertionError('regexp ' + re + ' does not match: ' + string));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // ARRAY
+
+ assert.inArray = function(expected, array) {
+ var found = false;
+ _.each(array, function(element){
+ if (_.isEqual(expected, element)){
+ found = true;
+ }
+ });
+ if (!found){
+ fail(new AssertionError('expected: ' + formatTestValue(expected) + ' not found in array: ' + formatTestValue(array)),
+ {Expected: expected, 'Array': array});
+ }
+ },
+
+ assert.notInArray = function(expected, array) {
+ var found = false;
+ _.each(array, function(element){
+ if (_.isEqual(expected, element)){
+ found = true;
+ }
+ });
+ if (found){
+ fail(new AssertionError('unexpected: ' + formatTestValue(expected) + ' found in array: ' + formatTestValue(array)),
+ {Expected: expected, 'Array': array});
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // OBJECTS
+
+ assert.hasKey = function (key, object) {
+ if (!(key in object)) {
+ fail(new AssertionError('Key ' + formatTestValue(key) + ' is not in object: ' + formatTestValue(object)));
+ }
+ },
+
+ assert.notHasKey = function (key, object) {
+ if (key in object) {
+ fail(new AssertionError('Unexpected key ' + formatTestValue(key) + ' is found in object: ' + formatTestValue(object)));
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // EXCEPTIONS
+
+ assert['throws'] = function(exception, fn) {
+ try {
+ fn();
+ } catch (e) {
+ if (e instanceof exception) {
+ return e;
+ }
+ fail(new AssertionError('Expected to throw "' + exception.name + '", actually threw: ' + formatTestValue(e) + ': ' + e.message),
+ {Expected: exception, Actual: e});
+ }
+ throw new AssertionError('did not throw');
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // TYPE
+
+ assert['instanceof'] = function(actual, type) {
+ if(!(actual instanceof type)) {
+ fail(new AssertionError(formatTestValue(actual) + ' not instance of ' + formatTestValue(type)),
+ {Type: type, Actual: actual});
+ }
+ },
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // DOM ASSERTIONS
+
+ // TODO: lift into separate file?
+ assert.dom = {
+ present: function(domElement){
+ if (!$(domElement).length) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' should be present'));
+ }
+ },
+
+ notPresent: function(selector){
+ assert.equal(0, $(selector).length);
+ },
+
+ hasTag: function(tag, domElement) {
+ var elementTag = $(domElement)[0].tagName.toLowerCase();
+ if (elementTag !== tag.toLowerCase()) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to have tag name ' + formatTestValue(tag) + ', was ' + formatTestValue(elementTag) + ' instead'));
+ }
+ },
+
+ hasClass: function(className, domElement) {
+ if (!$(domElement).hasClass(className)){
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to have class '+ formatTestValue(className) + ', has ' + formatTestValue($(domElement).attr('class')) + ' instead'));
+ }
+ },
+
+ notHasClass: function(className, domElement) {
+ assert.dom.present(domElement); // if domElement is empty, .hasClass will always return false
+ if ($(domElement).hasClass(className)){
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected NOT to have class '+ formatTestValue(className)));
+ }
+ },
+
+ hasAttribute: function(attributeName, selector) {
+ assert['true']($(selector).is('[' + attributeName + ']'));
+ },
+
+ notHasAttribute: function(attributeName, selector) {
+ assert.dom.present(selector);
+ assert['false']($(selector).is('[' + attributeName + ']'));
+ },
+
+ attr: function(value, attributeName, selector) {
+ assert.equal(value, $(selector).attr(attributeName));
+ },
+
+ attributeValues: function (values, selector) {
+ var $el = $(selector);
+ _(values).each(function (val, key) {
+ assert.equal(val, $el.attr(key));
+ });
+ },
+
+ text: function(expected, selector) {
+ assert.equal(expected, $(selector).text());
+ },
+
+ value: function(expected, selector) {
+ assert.equal(expected, $(selector).val());
+ },
+
+ count: function(elementCount, selector) {
+ assert.equal(elementCount, $(selector).length);
+ },
+
+ visible: function(domElement) {
+ if (!$(domElement).is(':visible')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be visible'));
+ }
+ },
+
+ notVisible: function(domElement) {
+ assert.dom.present(domElement);
+ if ($(domElement).is(':visible')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be NOT visible'));
+ }
+ },
+
+ disabled: function(domElement) {
+ if (!$(domElement).is(':disabled')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be disabled'));
+ }
+ },
+
+ enabled: function(domElement) {
+ if (!$(domElement).is(':enabled')) {
+ fail(new AssertionError(decipherDomElement(domElement) + ' expected to be enabled'));
+ }
+ },
+
+ focused: function(selector) {
+ var expected = $(selector)[0];
+ var actual = document.activeElement;
+ if (expected !== actual) {
+ throw new AssertionError(actual.outerHTML + ' has focus. expected: ' + expected.outerHTML);
+ }
+ },
+
+ notFocused: function(selector) {
+ var expected = $(selector)[0];
+ var actual = document.activeElement;
+ if (expected === actual) {
+ throw new AssertionError(expected.outerHTML + ' expected not to have focus.');
+ }
+ },
+
+ html: function(expected, selector) {
+ assert.equal(expected, $(selector).html());
+ },
+
+ css: function(expected, propertyName, selector) {
+ assert.equal(expected, $(selector).css(propertyName));
+ },
+
+ empty: function(selectorOrJQueryObject) {
+ var el = selectorOrJQueryObject;
+ assert.dom.present(el);
+ if (!$(el).is(':empty')) {
+ fail(new AssertionError(decipherDomElement(el) + ' expected to be empty'));
+ }
+ },
+
+ notEmpty: function(selectorOrJQueryObject) {
+ var el = selectorOrJQueryObject;
+ assert.dom.present(el);
+ if ($(el).is(':empty')) {
+ fail(new AssertionError(decipherDomElement(el) + ' expected NOT to be empty'));
+ }
+ }
+ }
+// };
+
+ function decipherDomElement(selectorOrJQueryObject) {
+ if (typeof selectorOrJQueryObject === 'string') {
+ return 'Selector ' + formatTestValue(selectorOrJQueryObject);
+ } else if (typeof selectorOrJQueryObject === 'object') {
+ return "'" + selectorOrJQueryObject[0] + "'";
+ }
+ }
+
+ var g = 'undefined' === typeof window ? global : window;
+
+ // synonyms
+ assert.equals = assert.equal;
+ assert.notEquals = assert.notEqual;
+ assert['null'] = assert.equal.bind(null, null);
+ assert.notNull = assert.notEqual.bind(null, null);
+ assert['undefined'] = assert.equal.bind(null, undefined);
+ assert.notUndefined = assert.notEqual.bind(null, undefined);
+
+ // ES3 synonyms
+ assert.false_ = assert['false'];
+ assert.true_ = assert['true'];
+
+ g.registerSuperFixture = registerSuperFixture;
+ g.test = test;
+ g.run_all = run_all;
+ g.fixture = fixture;
+// g.repr = IMVU.repr;
+ g.AssertionError = AssertionError;
+ g.assert = assert;
+ g.test = test;
+ g.TEST_MAX_OUTPUT_SIZE = 1024;
+
+ g.setTimeout = function(fn, time) {
+ if (time === 1 || time === 0){
+ fn();
+ return 0;
+ }
+ throw new AssertionError("Don't call setTimeout in tests. Use fakes.");
+ };
+
+ g.setInterval = function() {
+ throw new AssertionError("Don't call setInterval in tests. Use fakes.");
+ };
+
+ if (typeof process !== 'undefined') {
+ process.nextTick = function() {
+ throw new AssertionError("Don't call process.nextTick in tests. Use fakes.");
+ };
+ }
+
+ Math.random = function() {
+ throw new AssertionError("Don't call Math.random in tests. Use fakes.");
+ };
+
+ g.requestAnimationFrame = function() {
+ throw new AssertionError("Don't call requestAnimationFrame in tests. Use fakes.");
+ };
+//})();
+
+// Emscripten runner starts all tests from this function.
+// IMVU runner uses a separate runner & reporting mechanism.
+function run_all_tests() {
+ function report_to_stdout(msg) {
+ if (msg.type == "test-complete")
+ console.log(msg.name + ": " + msg.verdict);
+ }
+ run_all(report_to_stdout);
+}
+
+// Signal the embind test suite that it is being run from the Emscripten python test runner and not the
+// IMVU test runner.
+var INVOKED_FROM_EMSCRIPTEN_TEST_RUNNER = 1;
diff --git a/tests/embind/underscore-1.4.2.js b/tests/embind/underscore-1.4.2.js new file mode 100755 index 00000000..dd274095 --- /dev/null +++ b/tests/embind/underscore-1.4.2.js @@ -0,0 +1,1200 @@ +// Underscore.js 1.4.2 +// http://underscorejs.org +// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore may be freely distributed under the MIT license. + +//(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `global` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + unshift = ArrayProto.unshift, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root['_'] = _; + } + + // Current version. + _.VERSION = '1.4.2'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, l = obj.length; i < l; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + for (var key in obj) { + if (_.has(obj, key)) { + if (iterator.call(context, obj[key], key, obj) === breaker) return; + } + } + } + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results[results.length] = iterator.call(context, value, index, list); + }); + return results; + }; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError('Reduce of empty array with no initial value'); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError('Reduce of empty array with no initial value'); + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + each(obj, function(value, index, list) { + if (!iterator.call(context, value, index, list)) results[results.length] = value; + }); + return results; + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + var found = false; + if (obj == null) return found; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + found = any(obj, function(value) { + return value === target; + }); + return found; + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + return _.map(obj, function(value) { + return (_.isFunction(method) ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, function(value){ return value[key]; }); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // with specific `key:value` pairs. + _.where = function(obj, attrs) { + if (_.isEmpty(attrs)) return []; + return _.filter(obj, function(value) { + for (var key in attrs) { + if (attrs[key] !== value[key]) return false; + } + return true; + }); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See: https://bugs.webkit.org/show_bug.cgi?id=80797 + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return -Infinity; + var result = {computed : -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed >= result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return Infinity; + var result = {computed : Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Shuffle an array. + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + return _.isFunction(value) ? value : function(obj){ return obj[value]; }; + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, value, context) { + var iterator = lookupIterator(value); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value : value, + index : index, + criteria : iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index < right.index ? -1 : 1; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(obj, value, context, behavior) { + var result = {}; + var iterator = lookupIterator(value); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + (_.has(result, key) ? result[key] : (result[key] = [])).push(value); + }); + }; + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = function(obj, value, context) { + return group(obj, value, context, function(result, key, value) { + if (!_.has(result, key)) result[key] = 0; + result[key]++; + }); + }; + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = iterator == null ? _.identity : lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely convert anything iterable into a real, live array. + _.toArray = function(obj) { + if (!obj) return []; + if (obj.length === +obj.length) return slice.call(obj); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + return (n != null) && !guard ? slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if ((n != null) && !guard) { + return slice.call(array, Math.max(array.length - n, 0)); + } else { + return array[array.length - 1]; + } + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, function(value){ return !!value; }); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + each(input, function(value) { + if (_.isArray(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Return a completely flattened version of an array. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator, context) { + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(concat.apply(ArrayProto, arguments)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var args = slice.call(arguments); + var length = _.max(_.pluck(args, 'length')); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(args, "" + i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + var result = {}; + for (var i = 0, l = list.length; i < l; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, l = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < l; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var len = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(len); + + while(idx < len) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Reusable constructor function for prototype setting. + var ctor = function(){}; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Binding with arguments is also known as `curry`. + // Delegates to **ECMAScript 5**'s native `Function.bind` if available. + // We check for `func.bind` first, to fail fast when `func` is undefined. + _.bind = function bind(func, context) { + var bound, args; + if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; + }; + }; + + // Bind all of an object's methods to that object. Useful for ensuring that + // all callbacks defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length == 0) funcs = _.functions(obj); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. + _.throttle = function(func, wait) { + var context, args, timeout, throttling, more, result; + var whenDone = _.debounce(function(){ more = throttling = false; }, wait); + return function() { + context = this; args = arguments; + var later = function() { + timeout = null; + if (more) { + result = func.apply(context, args); + } + whenDone(); + }; + if (!timeout) timeout = setTimeout(later, wait); + if (throttling) { + more = true; + } else { + throttling = true; + result = func.apply(context, args); + } + whenDone(); + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments; + var later = function() { + timeout = null; + if (!immediate) result = func.apply(context, args); + }; + var callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) result = func.apply(context, args); + return result; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return function() { + var args = [func]; + push.apply(args, arguments); + return wrapper.apply(this, args); + }; + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + if (times <= 0) return func(); + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key; + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var values = []; + for (var key in obj) if (_.has(obj, key)) values.push(obj[key]); + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var pairs = []; + for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]); + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key; + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + for (var prop in source) { + if (obj[prop] == null) obj[prop] = source[prop]; + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor))) { + return false; + } + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return _.isNumber(obj) && isFinite(obj); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + // Run a function **n** times. + _.times = function(n, iterator, context) { + for (var i = 0; i < n; i++) iterator.call(context, i); + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + (0 | Math.random() * (max - min + 1)); + }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named property is a function then invoke it; + // otherwise, return it. + _.result = function(object, property) { + if (object == null) return null; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name){ + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = idCounter++; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + source += + escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" : + interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" : + evaluate ? "';\n" + evaluate + "\n__p+='" : ''; + index = offset + match.length; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + var render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + +//}).call(this); diff --git a/tests/runner.py b/tests/runner.py index fd9f7842..fa984f05 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -10241,58 +10241,12 @@ f.close() print args, fail self.clear() try_delete(self.in_dir('a.out.js')) - Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'embind_test.js')] + args, stderr=PIPE if fail else None).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'), '--post-js', path_from_root('tests', 'embind', 'underscore-1.4.2.js'), '--post-js', path_from_root('tests', 'embind', 'imvu_test_adapter.js'), '--post-js', path_from_root('tests', 'embind', 'embind.test.js')] + args, stderr=PIPE if fail else None).communicate() assert os.path.exists(self.in_dir('a.out.js')) == (not fail) if not fail: - output = run_js(self.in_dir('a.out.js')) - self.assertContained('''fixture: embind ---test: test value creation ---test: test passthrough ---test: test void return converts to undefined ---test: test booleans can be marshalled ---test: test convert double to unsigned ---test: test get length of array ---test: test add a bunch of things ---test: test sum array ---test: test strings ---test: test no memory leak when passing strings in by const reference -fixture: classes ---test: test class instance ---test: test class methods ---test: test can't call methods on deleted class instances ---test: test isinstance ---test: test can return class instances by value ---test: test can pass class instances to functions by reference ---test: test can access struct fields ---test: test can set struct fields ---test: test assignment returns value ---test: test assigning string to integer raises TypeError ---test: test can return tuples by value ---test: test tuples can contain tuples ---test: test can pass tuples by value ---test: test can return structs by value ---test: test can pass structs by value ---test: test can pass and return tuples in structs ---test: test can clone handles ---test: test can't clone if already deleted ---test: test moving handles is a clone+delete ---test: test StringHolder -fixture: embind enumerations ---test: test can compare enumeration values ---test: test repr includes enum value ---test: test instanceof ---test: test can pass and return enumeration values to functions -fixture: C++11 enum class ---test: test can compare enumeration values ---test: test repr includes enum value ---test: test instanceof ---test: test can pass and return enumeration values to functions -fixture: emval call tests ---test: test can call functions from C++ -fixture: interfaces ---test: test can wrap JS object in native interface ---test: test can pass arguments and return complicated values ---test: test can call interface methods that return nothing''', output) + output = run_js(self.in_dir('a.out.js'), stdout=PIPE, stderr=PIPE, full_output=True) + print >> sys.stderr, output + assert "FAIL" not in output def test_llvm_nativizer(self): try: |