From ee46fc218211b5b71c525129ddc6de214b99422e Mon Sep 17 00:00:00 2001 From: Mike Gerwitz Date: Tue, 4 Feb 2014 23:55:24 -0500 Subject: [PATCH] Began testing class subtyping with mixins --- test/Trait/AbstractTest.js | 38 ++++++++++++++++++++++ test/Trait/MixedExtendTest.js | 59 +++++++++++++++++++++++++++++++++++ test/Trait/VirtualTest.js | 49 +++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 test/Trait/MixedExtendTest.js diff --git a/test/Trait/AbstractTest.js b/test/Trait/AbstractTest.js index 130d971..742b5df 100644 --- a/test/Trait/AbstractTest.js +++ b/test/Trait/AbstractTest.js @@ -186,4 +186,42 @@ require( 'common' ).testCase( "Crap; order matters?!" ); }, + + + /** + * If some trait T used by abstract class C defines abstract method M, + * then some subtype C' of C should be able to provide a concrete + * definition of M such that T.M() invokes C'.M. + */ + 'Abstract method inherited from trait can be implemented by subtype': + function() + { + var T = this.Sut( + { + 'public doFoo': function() + { + // should invoke the concrete implementation + this.foo(); + }, + + 'abstract protected foo': [], + } ); + + var called = false; + + // C is a concrete class that extends an abstract class that uses + // trait T + var C = this.AbstractClass.use( T ).extend( {} ) + .extend( + { + // concrete definition that should be invoked by T.doFoo + 'protected foo': function() + { + called = true; + }, + } ); + + C().doFoo(); + this.assertOk( called ); + }, } ); diff --git a/test/Trait/MixedExtendTest.js b/test/Trait/MixedExtendTest.js new file mode 100644 index 0000000..76f8cb6 --- /dev/null +++ b/test/Trait/MixedExtendTest.js @@ -0,0 +1,59 @@ +/** + * Tests extending a class that mixes in traits + * + * 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' ); + }, + + + /** + * The supertype should continue to work as it would without the + * subtype, which means that the supertype's traits should still be + * available. Note that ease.js does not (at least at the time of + * writing this test) check to see if a trait is no longer accessible + * due to overrides, and so a supertype's traits will always be + * instantiated. + */ + 'Subtype instantiates traits of supertype': function() + { + var called = false; + + var T = this.Sut( + { + foo: function() { called = true; }, + } ); + + // C is a subtype of a class that mixes in T + var C = this.Class.use( T ).extend( {} ) + .extend( + { + // ensure that there is no ctor-dependent trait stuff + __construct: function() {}, + } ); + + C().foo(); + this.assertOk( called ); + }, +} ); diff --git a/test/Trait/VirtualTest.js b/test/Trait/VirtualTest.js index abcf44a..df02742 100644 --- a/test/Trait/VirtualTest.js +++ b/test/Trait/VirtualTest.js @@ -134,4 +134,53 @@ require( 'common' ).testCase( C().doFoo(); this.assertOk( called ); }, + + + /** + * If a supertype mixes in a trait that provides a virtual method, a + * subtype should be able to provide its own concrete implementation. + * This is especially important to test in the case where a trait + * invokes its own virtual method---we must ensure that the message is + * properly passed to the subtype's override. + * + * For a more formal description of a similar matter, see the + * AbstractTest case; indeed, we're trying to mimic the same behavior + * that we'd expect with abstract methods. + */ + 'Subtype can override virtual method of trait mixed into supertype': + function() + { + var _self = this; + + var T = this.Sut( + { + 'public doFoo': function() + { + // this call should be passed to any overrides + return this.foo(); + }, + + // this is the one we'll try to override + 'virtual protected foo': function() + { + _self.fail( true, false, "Method not overridden." ); + }, + } ); + + var called = false; + + // C is a subtype of a class that implements T + var C = this.Class.use( T ).extend( {} ) + .extend( + { + // this should be called instead of T.foo + 'override protected foo': function() + { + called = true; + }, + } ); + + C().doFoo(); + this.assertOk( called ); + }, } );