text: module({
      Emscripten: '../../../../build/embind_test.js',
  }, function(imports) {
      var cm = imports.Emscripten;
  
      var CheckForLeaks = fixture("check for leaks", function() {
          this.setUp(function() {
              cm.setDelayFunction(undefined);
  
              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() {
              cm.flushPendingDeletes();
              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('Expected null or instance of Derived, got an instance of Base2', e.message);
              a.delete();
  
              // Base1 and Base2 both have the method 'getField()' exposed - make sure
              // that calling the Base2 function with a 'this' instance of Base1 doesn't accidentally work!
              var b = new cm.Base1;
              var e = assert.throws(cm.BindingError, function() {
                  cm.Base2.prototype.getField.call(b);
              });
              assert.equal('Expected null or instance of Base2, got an instance of Base1', e.message);
              b.delete();
          });
  
          test("calling method with invalid this throws error", function() {
              var e = assert.throws(cm.BindingError, function() {
                  cm.Derived.prototype.setMember.call(undefined, "foo");
              });
              assert.equal('Cannot pass "[object global]" as a Derived*', e.message);
  
              var e = assert.throws(cm.BindingError, function() {
                  cm.Derived.prototype.setMember.call(true, "foo");
              });
              assert.equal('Cannot pass "true" as a Derived*', e.message);
  
              var e = assert.throws(cm.BindingError, function() {
                  cm.Derived.prototype.setMember.call(null, "foo");
              });
              assert.equal('Cannot pass "[object global]" as a Derived*', e.message);
  
              var e = assert.throws(cm.BindingError, function() {
                  cm.Derived.prototype.setMember.call(42, "foo");
              });
              assert.equal('Cannot pass "42" as a Derived*', e.message);
  
              var e = assert.throws(cm.BindingError, function() {
                  cm.Derived.prototype.setMember.call("this", "foo");
              });
              assert.equal('Cannot pass "this" as a Derived*', e.message);
  
              var e = assert.throws(cm.BindingError, function() {
                  cm.Derived.prototype.setMember.call({}, "foo");
              });
              assert.equal('Cannot pass "[object Object]" as a Derived*', 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("string", function() {
          test("non-ascii strings", function() {
              var expected = '';
              for (var i = 0; i < 128; ++i) {
                  expected += String.fromCharCode(128 + i);
              }
              assert.equal(expected, cm.get_non_ascii_string());
          });
  
          test("passing non-8-bit strings from JS to std::string throws", function() {
              assert.throws(cm.BindingError, function() {
                  cm.emval_test_take_and_return_std_string("\u1234");
              });
          });
  
          test("can't pass integers as strings", function() {
              var e = assert.throws(cm.BindingError, function() {
                  cm.emval_test_take_and_return_std_string(10);
              });
          });
  
          test("can pass Uint8Array to std::string", function() {
              var e = cm.emval_test_take_and_return_std_string(new Uint8Array([65, 66, 67, 68]));
              assert.equal('ABCD', e);
          });
  
          test("can pass Int8Array to std::string", function() {
              var e = cm.emval_test_take_and_return_std_string(new Int8Array([65, 66, 67, 68]));
              assert.equal('ABCD', e);
          });
  
          test("can pass ArrayBuffer to std::string", function() {
              var e = cm.emval_test_take_and_return_std_string((new Int8Array([65, 66, 67, 68])).buffer);
              assert.equal('ABCD', e);
          });
  
          test("non-ascii wstrings", function() {
              var expected = String.fromCharCode(10) +
                  String.fromCharCode(1234) +
                  String.fromCharCode(2345) +
                  String.fromCharCode(65535);
              assert.equal(expected, cm.get_non_ascii_wstring());
          });
  
          test("passing unicode string into C++", function() {
              var expected = String.fromCharCode(10) +
                  String.fromCharCode(1234) +
                  String.fromCharCode(2345) +
                  String.fromCharCode(65535);
              assert.equal(expected, cm.take_and_return_std_wstring(expected));
          });
      });
  
      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("can pass booleans as integers", function() {
              assert.equal(1, cm.emval_test_as_unsigned(true));
              assert.equal(0, cm.emval_test_as_unsigned(false));
          });
  
          test("can pass booleans as floats", function() {
              assert.equal(2, cm.const_ref_adder(true, true));
          });
  
          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("access multiple class ctors", function() {
              var a = new cm.MultipleCtors(10);
              assert.equal(a.WhichCtorCalled(), 1);
              var b = new cm.MultipleCtors(20, 20);
              assert.equal(b.WhichCtorCalled(), 2);
              var c = new cm.MultipleCtors(30, 30, 30);
              assert.equal(c.WhichCtorCalled(), 3);
              a.delete();
              b.delete();
              c.delete();
          });
  
          test("access multiple smart ptr ctors", function() {
              var a = new cm.MultipleSmartCtors(10);
              assert.equal(a.WhichCtorCalled(), 1);
              var b = new cm.MultipleCtors(20, 20);
              assert.equal(b.WhichCtorCalled(), 2);
              a.delete();
              b.delete();
          });
  
          test("wrong number of constructor arguments throws", function() {
              assert.throws(cm.BindingError, function() { new cm.MultipleCtors(); });
              assert.throws(cm.BindingError, function() { new cm.MultipleCtors(1,2,3,4); });
          });
  
          test("overloading of free functions", function() {
              var a = cm.overloaded_function(10);
              assert.equal(a, 1);
              var b = cm.overloaded_function(20, 20);
              assert.equal(b, 2);
          });
  
          test("wrong number of arguments to an overloaded free function", function() {
              assert.throws(cm.BindingError, function() { cm.overloaded_function(); });
              assert.throws(cm.BindingError, function() { cm.overloaded_function(30, 30, 30); });
          });
  
          test("overloading of class member functions", function() {
              var foo = new cm.MultipleOverloads();
              assert.equal(foo.Func(10), 1);
              assert.equal(foo.WhichFuncCalled(), 1);
              assert.equal(foo.Func(20, 20), 2);
              assert.equal(foo.WhichFuncCalled(), 2);
              foo.delete();
          });
  
          test("wrong number of arguments to an overloaded class member function", function() {
              var foo = new cm.MultipleOverloads();
              assert.throws(cm.BindingError, function() { foo.Func(); });
              assert.throws(cm.BindingError, function() { foo.Func(30, 30, 30); });
              foo.delete();
          });
  
          test("wrong number of arguments to an overloaded class static function", function() {
              assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(); });
              assert.throws(cm.BindingError, function() { cm.MultipleOverloads.StaticFunc(30, 30, 30); });
          });
  
          test("overloading of derived class member functions", function() {
              var foo = new cm.MultipleOverloadsDerived();
  
              // NOTE: In C++, default lookup rules will hide overloads from base class if derived class creates them.
              // In JS, we make the base class overloads implicitly available. In C++, they would need to be explicitly
              // invoked, like foo.MultipleOverloads::Func(10);
              assert.equal(foo.Func(10), 1);
              assert.equal(foo.WhichFuncCalled(), 1);
              assert.equal(foo.Func(20, 20), 2);
              assert.equal(foo.WhichFuncCalled(), 2);
  
              assert.equal(foo.Func(30, 30, 30), 3);
              assert.equal(foo.WhichFuncCalled(), 3);
              assert.equal(foo.Func(40, 40, 40, 40), 4);
              assert.equal(foo.WhichFuncCalled(), 4);
              foo.delete();
          });
  
          test("overloading of class static functions", function() {
              assert.equal(cm.MultipleOverloads.StaticFunc(10), 1);
              assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 1);
              assert.equal(cm.MultipleOverloads.StaticFunc(20, 20), 2);
              assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 2);
          });
  
          test("overloading of derived class static functions", function() {
              assert.equal(cm.MultipleOverloadsDerived.StaticFunc(30, 30, 30), 3);
              // TODO: Cannot access static member functions of a Base class via Derived.
  //            assert.equal(cm.MultipleOverloadsDerived.WhichStaticFuncCalled(), 3);
              assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 3);
              assert.equal(cm.MultipleOverloadsDerived.StaticFunc(40, 40, 40, 40), 4);
              // TODO: Cannot access static member functions of a Base class via Derived.
  //            assert.equal(cm.MultipleOverloadsDerived.WhichStaticFuncCalled(), 4);
              assert.equal(cm.MultipleOverloads.WhichStaticFuncCalled(), 4);
          });
  /*
          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();
              var b = a.clone();
              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 properties can be methods", function() {
              var a = {};
              var b = {foo: 'foo'};
              var c = new cm.ValHolder(a);
              assert.equal(a, c.val);
              c.val = b;
              assert.equal(b, c.val);
              c.delete();
          });
  
          test("class properties can be read-only", function() {
              var a = {};
              var h = new cm.ValHolder(a);
              assert.equal(a, h.val_readonly);
              var e = assert.throws(cm.BindingError, function() {
                  h.val_readonly = 10;
              });
              assert.equal('ValHolder.val_readonly is a read-only property', e.message);
              h.delete();
          });
  
          test("read-only member field", function() {
              var a = new cm.HasReadOnlyProperty(10);
              assert.equal(10, a.i);
              var e = assert.throws(cm.BindingError, function() {
                  a.i = 20;
              });
              assert.equal('HasReadOnlyProperty.i is a read-only property', e.message);
              a.delete();
          });
  
          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, 4], c);
          });
  
          test("tuples can contain tuples", function() {
              var c = cm.emval_test_return_TupleVectorTuple();
              assert.deepEqual([[1, 2, 3, 4]], c);
          });
  
          test("can pass tuples by value", function() {
              var c = cm.emval_test_take_and_return_TupleVector([4, 5, 6, 7]);
              assert.deepEqual([4, 5, 6, 7], c);
          });
  
          test("can return structs by value", function() {
              var c = cm.emval_test_return_StructVector();
              assert.deepEqual({x: 1, y: 2, z: 3, w: 4}, c);
          });
  
          test("can pass structs by value", function() {
              var c = cm.emval_test_take_and_return_StructVector({x: 4, y: 5, z: 6, w: 7});
              assert.deepEqual({x: 4, y: 5, z: 6, w: 7}, c);
          });
  
          test("can pass and return tuples in structs", function() {
              var d = cm.emval_test_take_and_return_TupleInStruct({field: [1, 2, 3, 4]});
              assert.deepEqual({field: [1, 2, 3, 4]}, 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.true(b.isAliasOf(c));
              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, 4], tv);
                  assert.deepEqual({x: 1.25, y: 2.5, z: 3.75, w:4}, sv);
              }, 10, 1.5, [1.25, 2.5, 3.75, 4], {x: 1.25, y: 2.5, z: 3.75, w:4});
              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("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();
          });
  
          test("can implement optional methods in JavaScript", function() {
              var expected = "my JS string";
              function MyImplementation() {
                  this.rv = expected;
              }
              MyImplementation.prototype.optionalMethod = function() {
                  return this.rv;
              };
  
              var impl = cm.AbstractClass.implement(new MyImplementation);
              assert.equal(expected, impl.optionalMethod(expected));
              assert.equal(expected, cm.callOptionalMethod(impl, expected));
              impl.delete();
          });
  
          test("if not implemented then optional method runs default", function() {
              var impl = cm.AbstractClass.implement({});
              assert.equal("optionalfoo", impl.optionalMethod("foo"));
              assert.equal("optionalfoo", cm.callOptionalMethod(impl, "foo"));
              impl.delete();
          });
  
          test("returning null shared pointer from interfaces implemented in JS code does not leak", function() {
              var impl = cm.AbstractClass.implement({
                  returnsSharedPtr: function() {
                      return null;
                  }
              });
              cm.callReturnsSharedPtrMethod(impl);
              impl.delete();
              // Let the memory leak test superfixture check that no leaks occurred.
          });
  
          test("returning a new shared pointer from interfaces implemented in JS code does not leak", function() {
              var impl = cm.AbstractClass.implement({
                  returnsSharedPtr: function() {
                      return cm.embind_test_return_smart_derived_ptr().deleteLater();
                  }
              });
              cm.callReturnsSharedPtrMethod(impl);
              impl.delete();
              // Let the memory leak test superfixture check that no leaks occurred.
          });
  
          test("void methods work", function() {
              var saved = {};
              var impl = cm.AbstractClass.implement({
                  differentArguments: function(i, d, f, q, s) {
                      saved.i = i;
                      saved.d = d;
                      saved.f = f;
                      saved.q = q;
                      saved.s = s;
                  }
              });
  
              cm.callDifferentArguments(impl, 1, 2, 3, 4, "foo");
  
              assert.deepEqual(saved, {
                  i: 1,
                  d: 2,
                  f: 3,
                  q: 4,
                  s: "foo",
              });
  
              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: 12UnboundClass');
          });
  
          test("unbound base class produces error", function() {
              assertMessage(
                  function() {
                      cm.getHasUnboundBase();
                  },
                  'Cannot call getHasUnboundBase due to unbound types: 12UnboundClass');
          });
  
          test("construct of class with unbound base", function() {
              assertMessage(
                  function() {
                      new cm.HasUnboundBase;
                  }, 'Cannot construct HasUnboundBase due to unbound types: 12UnboundClass');
          });
  
          test("unbound constructor argument", function() {
              assertMessage(
                  function() {
                      new cm.HasConstructorUsingUnboundArgument(1);
                  },
                  'Cannot construct HasConstructorUsingUnboundArgument due to unbound types: 12UnboundClass');
          });
  
          test("unbound constructor argument of class with unbound base", function() {
              assertMessage(
                  function() {
                      new cm.HasConstructorUsingUnboundArgumentAndUnboundBase;
                  },
                  'Cannot construct HasConstructorUsingUnboundArgumentAndUnboundBase due to unbound types: 18SecondUnboundClass');
          });
  
          test('class function with unbound argument', function() {
              var x = new cm.BoundClass;
              assertMessage(
                  function() {
                      x.method();
                  }, 'Cannot call BoundClass.method due to unbound types: 12UnboundClass');
              x.delete();
          });
  
          test('class class function with unbound argument', function() {
              assertMessage(
                  function() {
                      cm.BoundClass.classfunction();
                  }, 'Cannot call BoundClass.classfunction due to unbound types: 12UnboundClass');
          });
  
          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: 12UnboundClass');
              assertMessage(
                  function() {
                      x.property = 10;
                  }, 'Cannot access BoundClass.property due to unbound types: 12UnboundClass');
              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);
      });
  
      BaseFixture.extend("constants", function() {
          assert.equal(10, cm.INT_CONSTANT);
  
          assert.equal(1, cm.STATIC_CONST_INTEGER_VALUE_1);
          assert.equal(1000, cm.STATIC_CONST_INTEGER_VALUE_1000);
  
          assert.equal("some string", cm.STRING_CONSTANT);
          assert.deepEqual([1, 2, 3, 4], cm.VALUE_ARRAY_CONSTANT);
          assert.deepEqual({x:1,y:2,z:3,w:4}, cm.VALUE_OBJECT_CONSTANT);
      });
  
      BaseFixture.extend("object handle comparison", function() {
          var e = new cm.ValHolder("foo");
          var f = new cm.ValHolder("foo");
          assert.false(e.isAliasOf(undefined));
          assert.false(e.isAliasOf(10));
          assert.true(e.isAliasOf(e));
          assert.false(e.isAliasOf(f));
          assert.false(f.isAliasOf(e));
          e.delete();
          f.delete();
      });
  
      BaseFixture.extend("derived-with-offset types compare with base", function() {
          var e = new cm.DerivedWithOffset;
          var f = cm.return_Base_from_DerivedWithOffset(e);
          assert.true(e.isAliasOf(f));
          assert.true(f.isAliasOf(e));
          e.delete();
          f.delete();
      });
  
      BaseFixture.extend("memory view", function() {
          test("can pass memory view from C++ to JS", function() {
              var views = [];
              cm.callWithMemoryView(function(view) {
                  views.push(view);
              });
              assert.equal(3, views.length);
  
              assert.instanceof(views[0], Uint8Array);
              assert.equal(8, views[0].length);
              assert.deepEqual([0, 1, 2, 3, 4, 5, 6, 7], [].slice.call(new Uint8Array(views[0])));
  
              assert.instanceof(views[1], Float32Array);
              assert.equal(4, views[1].length);
              assert.deepEqual([1.5, 2.5, 3.5, 4.5], [].slice.call(views[1]));
  
              assert.instanceof(views[2], Int16Array);
              assert.equal(4, views[2].length);
              assert.deepEqual([1000, 100, 10, 1], [].slice.call(views[2]));
          });
      });
  
      BaseFixture.extend("delete pool", function() {
          test("can delete objects later", function() {
              var v = new cm.ValHolder({});
              v.deleteLater();
              assert.deepEqual({}, v.getVal());
              cm.flushPendingDeletes();
              assert.throws(cm.BindingError, function() {
                  v.getVal();
              });
          });
  
          test("calling deleteLater twice is an error", function() {
              var v = new cm.ValHolder({});
              v.deleteLater();
              assert.throws(cm.BindingError, function() {
                  v.deleteLater();
              });
          });
  
          test("deleteLater returns the object", function() {
              var v = (new cm.ValHolder({})).deleteLater();
              assert.deepEqual({}, v.getVal());
          });
  
          test("deleteLater throws if object is already deleted", function() {
              var v = new cm.ValHolder({});
              v.delete();
              assert.throws(cm.BindingError, function() {
                  v.deleteLater();
              });
          });
  
          test("delete throws if object is already scheduled for deletion", function() {
              var v = new cm.ValHolder({});
              v.deleteLater();
              assert.throws(cm.BindingError, function() {
                  v.delete();
              });
          });
  
          test("deleteLater invokes delay function", function() {
              var runLater;
              cm.setDelayFunction(function(fn) {
                  runLater = fn;
              });
  
              var v = new cm.ValHolder({});
              assert.false(runLater);
              v.deleteLater();
              assert.true(runLater);
              assert.false(v.isDeleted());
              runLater();
              assert.true(v.isDeleted());
          });
  
          test("deleteLater twice invokes delay function once", function() {
              var count = 0;
              var runLater;
              cm.setDelayFunction(function(fn) {
                  ++count;
                  runLater = fn;
              });
  
              (new cm.ValHolder({})).deleteLater();
              (new cm.ValHolder({})).deleteLater();
              assert.equal(1, count);
              runLater();
              (new cm.ValHolder({})).deleteLater();
              assert.equal(2, count);
          });
  
          test('The delay function is immediately invoked if the deletion queue is not empty', function() {
              (new cm.ValHolder({})).deleteLater();
              var count = 0;
              cm.setDelayFunction(function(fn) {
                  ++count;
              });
              assert.equal(1, count);
          });
  
          // The idea is that an interactive application would
          // periodically flush the deleteLater queue by calling
          //
          // setDelayFunction(function(fn) {
          //     setTimeout(fn, 0);
          // });
      });
  
      BaseFixture.extend("references", function() {
          test("JS object handles can be passed through to C++ by reference", function() {
              var sh = new cm.StringHolder("Hello world");
              assert.equal("Hello world", sh.get());
              cm.clear_StringHolder(sh);
              assert.equal("", sh.get());
              sh.delete();
          });
      });
  
      BaseFixture.extend("val::as from pointer to value", function() {
          test("calling as on pointer with value makes a copy", function() {
              var sh1 = new cm.StringHolder("Hello world");
              var sh2 = cm.return_StringHolder_copy(sh1);
              assert.equal("Hello world", sh1.get());
              assert.equal("Hello world", sh2.get());
              assert.false(sh1.isAliasOf(sh2));
              sh2.delete();
              sh1.delete();
          });
  
          test("calling function that returns a StringHolder", function() {
              var sh1 = new cm.StringHolder("Hello world");
              var sh2 = cm.call_StringHolder_func(function() {
                  return sh1;
              });
              assert.equal("Hello world", sh1.get());
              assert.equal("Hello world", sh2.get());
              assert.false(sh1.isAliasOf(sh2));
              sh2.delete();
              sh1.delete();
          });
      });
  
      BaseFixture.extend("mixin", function() {
          test("can call mixin method", function() {
              var a = new cm.DerivedWithMixin();
              assert.instanceof(a, cm.Base);
              assert.equal(10, a.get10());
              a.delete();
          });
      });
  
      test("returning a cached new shared pointer from interfaces implemented in JS code does not leak", function() {
          var derived = cm.embind_test_return_smart_derived_ptr();
          var impl = cm.AbstractClass.implement({
              returnsSharedPtr: function() {
                  return derived;
              }
          });
          cm.callReturnsSharedPtrMethod(impl);
          impl.delete();
          derived.delete();
          // Let the memory leak test superfixture check that no leaks occurred.
      });
  });
  
  /* global run_all_tests */
  // 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();
  