From 23557e7d5cb78809c5b3f6d51bf309666e030842 Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Wed, 6 Aug 2014 22:45:52 -0400 Subject: [PATCH 1/2] Interface/InteropTest case assertion refactoring This will allow multiple sub-assertions to be performed---see next commit. --- test/Interface/InteropTest.js | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/test/Interface/InteropTest.js b/test/Interface/InteropTest.js index 18b3596..6740d09 100644 --- a/test/Interface/InteropTest.js +++ b/test/Interface/InteropTest.js @@ -30,6 +30,16 @@ require( 'common' ).testCase( foo: [ 'a', 'b' ], bar: [ 'a' ], } ); + + this.assertICompat = function( I, inst ) + { + this.assertOk( I.isCompatible( inst ) ); + }; + + this.assertNotICompat = function( I, inst ) + { + this.assertOk( !I.isCompatible( inst ) ); + }; }, @@ -68,12 +78,14 @@ require( 'common' ).testCase( bar: function( a ) {}, }; + var p = new P(); + // instance should therefore be conforming - this.assertOk( this.I.isCompatible( new P() ) ); + this.assertICompat( this.I, p ); // ah but why stop there? (note that this implies that *any* object, // prototype or not, can conform to an interface) - this.assertOk( this.I.isCompatible( P.prototype ) ); + this.assertICompat( this.I, P.prototype ); }, @@ -89,8 +101,8 @@ require( 'common' ).testCase( foo: function( a, b ) {}, }; - this.assertOk( !( this.I.isCompatible( new P() ) ) ); - this.assertOk( !( this.I.isCompatible( P.prototype ) ) ); + this.assertNotICompat( this.I, new P() ); + this.assertNotICompat( this.I, P.prototype ); }, @@ -111,7 +123,7 @@ require( 'common' ).testCase( var obj = { foo: function( a ) {} }, I = this.Sut( { foo: [ 'a', 'b' ] } ); - this.assertOk( !( I.isCompatible( obj ) ) ); + this.assertNotICompat( I, obj ); }, @@ -124,7 +136,7 @@ require( 'common' ).testCase( var obj = { foo: function( a, b, c ) {} }, I = this.Sut( { foo: [ 'a', 'b' ] } ); - this.assertOk( I.isCompatible( obj ) ); + this.assertICompat( I, obj ); }, @@ -137,7 +149,7 @@ require( 'common' ).testCase( var obj = { foo: {} }, I = this.Sut( { foo: [] } ); - this.assertOk( !( I.isCompatible( obj ) ) ); + this.assertNotICompat( I, obj ); }, @@ -151,7 +163,7 @@ require( 'common' ).testCase( var obj = { foo: function() {}, bar: function() {} }, I = this.Sut( { foo: [] } ); - this.assertOk( I.isCompatible( obj ) ); + this.assertICompat( I, obj ); }, } ); From b9ba6388d20eba0615a90f918c525efe1666acea Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Wed, 6 Aug 2014 23:06:13 -0400 Subject: [PATCH 2/2] Interface.isInstanceOf will account for interop compatibility This is a bug fix. If the provided object's constructor is an ease.js type, then the conventional rules will apply (as mentioned in the test docblock and in the manual); however, if it's just a vanilla ECMAScript object, then the interop compatibility checks will be used instead. The manual already states that this is the case; unfortunately, it lies---this was apparently overlooked, and is a bug. --- lib/interface.js | 2 +- test/Interface/InteropTest.js | 42 ++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/lib/interface.js b/lib/interface.js index a8225c0..f3e9b5a 100644 --- a/lib/interface.js +++ b/lib/interface.js @@ -500,7 +500,7 @@ function _isInstanceOf( type, instance ) var meta; if ( !instance.__cid || !( meta = ClassBuilder.getMeta( proto ) ) ) { - return false; + return isCompat( type, instance ); } var implemented = meta.implemented, diff --git a/test/Interface/InteropTest.js b/test/Interface/InteropTest.js index 6740d09..ccfda98 100644 --- a/test/Interface/InteropTest.js +++ b/test/Interface/InteropTest.js @@ -23,7 +23,8 @@ require( 'common' ).testCase( { caseSetUp: function() { - this.Sut = this.require( 'interface' ); + this.Sut = this.require( 'interface' ); + this.Class = this.require( 'class' ); this.I = this.Sut( { @@ -34,11 +35,13 @@ require( 'common' ).testCase( this.assertICompat = function( I, inst ) { this.assertOk( I.isCompatible( inst ) ); + this.assertOk( this.Sut.isInstanceOf( I, inst ) ); }; this.assertNotICompat = function( I, inst ) { this.assertOk( !I.isCompatible( inst ) ); + this.assertOk( !this.Sut.isInstanceOf( I, inst ) ); }; }, @@ -165,5 +168,42 @@ require( 'common' ).testCase( this.assertICompat( I, obj ); }, + + + /** + * When an object is instantiated from an ease.js class, it does not + * matter if the interface is compatible: in order to be considered an + * instance some interface I, the instance's type must implement I; in + * this sense, ease.js' interface typing is strict, allowing *intent* to + * be conveyed. + * + * An example of why this is important can be found in the + * interoperability section of the manual. + */ + 'Objects can be compatible but not instances of interface': function() + { + // same API, different interface objects + var Ia = this.Sut( { foo: [] } ), + Ib = this.Sut( { foo: [] } ); + + var dfn = { foo: function() {} }, + Ca = this.Class.implement( Ia ).extend( dfn ), + Cb = this.Class.implement( Ib ).extend( dfn ); + + var ia = Ca(), + ib = Cb(); + + // clearly the two are compatible, regardless of their type + this.assertOk( Ia.isCompatible( ia ) ); + this.assertOk( Ia.isCompatible( ib ) ); + this.assertOk( Ib.isCompatible( ia ) ); + this.assertOk( Ib.isCompatible( ib ) ); + + // but ia is *not* an instance of Ib, nor ib of Ia + this.assertOk( this.Sut.isInstanceOf( Ia, ia ) ); + this.assertOk( !this.Sut.isInstanceOf( Ia, ib ) ); + this.assertOk( this.Sut.isInstanceOf( Ib, ib ) ); + this.assertOk( !this.Sut.isInstanceOf( Ib, ia ) ); + }, } );