diff --git a/test/Trait/DefinitionTest.js b/test/Trait/DefinitionTest.js index ad2897d..cbaff31 100644 --- a/test/Trait/DefinitionTest.js +++ b/test/Trait/DefinitionTest.js @@ -245,21 +245,4 @@ require( 'common' ).testCase( this.fail( false, true, "Mixin must fail on conflict: " + desc ); }, - - - 'Private class members are not accessible to used traits': function() - { - // TODO: this is not yet the case - }, - - - /** - * Traits will need to be able to keep and manipulate their own internal - * state. - */ - 'Private trait members are not accessible to containing class': - function() - { - // TODO: this is not yet the case - }, } ); diff --git a/test/Trait/ScopeTest.js b/test/Trait/ScopeTest.js new file mode 100644 index 0000000..b39ada2 --- /dev/null +++ b/test/Trait/ScopeTest.js @@ -0,0 +1,129 @@ +/** + * Tests trait scoping + * + * Copyright (C) 2014 Mike Gerwitz + * + * This file is part of GNU ease.js. + * + * ease.js is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +require( 'common' ).testCase( +{ + caseSetUp: function() + { + this.Sut = this.require( 'Trait' ); + this.Class = this.require( 'class' ); + }, + + + /** + * Since the private scope of classes and the traits that they use are + * disjoint, traits should never be able to access any private member of + * a class that uses it. + * + * The beauty of this is that we get this ``feature'' for free with + * our composition-based trait implementation. + */ + 'Private class members are not accessible to used traits': function() + { + var T = this.Sut( + { + // attempts to access C._priv + 'public getPriv': function() { return this._priv; }, + + // attempts to invoke C._privMethod + 'public invokePriv': function() { this._privMethod(); }, + } ); + + var inst = this.Class.use( T ).extend( + { + 'private _priv': 'foo', + 'private _privMethod': function() {}, + } )(); + + this.assertEqual( inst.getPriv(), undefined ); + this.assertThrows( function() + { + inst.invokePriv(); + }, Error ); + }, + + + /** + * Similar concept to the above---class and trait scopes are disjoint. + * This is particularily important, since traits will have no idea what + * other traits they will be mixed in with and therefore must be immune + * from nasty state clashes. + */ + 'Private trait members are not accessible to containing class': + function() + { + var T = this.Sut( + { + 'private _priv': 'bar', + 'private _privMethod': function() {}, + } ); + + // reverse of the previous test case + var inst = this.Class.use( T ).extend( + { + // attempts to access T._priv + 'public getPriv': function() { return this._priv; }, + + // attempts to invoke T._privMethod + 'public invokePriv': function() { this._privMethod(); }, + } )(); + + + this.assertEqual( inst.getPriv(), undefined ); + this.assertThrows( function() + { + inst.invokePriv(); + }, Error ); + }, + + + /** + * Since all scopes are disjoint, it would stand to reason that all + * traits should also have their own private scope independent of other + * traits that are mixed into the same class. This is also very + * important for the same reasons as the previous test---we cannot have + * state clashes between traits. + */ + 'Traits do not have access to each others\' private members': function() + { + var T1 = this.Sut( + { + 'private _priv1': 'foo', + 'private _privMethod1': function() {}, + } ), + T2 = this.Sut( + { + // attempts to access T1._priv1 + 'public getPriv': function() { return this._priv1; }, + + // attempts to invoke T1._privMethod1 + 'public invokePriv': function() { this._privMethod1(); }, + } ); + + var inst = this.Class.use( T1, T2 ).extend( {} )(); + + this.assertEqual( inst.getPriv(), undefined ); + this.assertThrows( function() + { + inst.invokePriv(); + }, Error ); + }, +} );