1
0
Fork 0

Merge branch 'master' into 'virtual/master'

- Resolved conflicts
closure/master
Mike Gerwitz 2011-06-30 23:00:13 -04:00
parent 72e5d481e6
commit 81fa2ae424
15 changed files with 1098 additions and 26 deletions

View File

@ -10,6 +10,9 @@ path_perf_test=${path_test}/perf
perf_tests := $(shell find "$(path_perf_test)" -name 'perf-*.js') perf_tests := $(shell find "$(path_perf_test)" -name 'perf-*.js')
src_js := index.js $(wildcard $(path_lib)/*.js)
src_tests := index.js $(wildcard $(path_test)/test-*)
path_doc := ./doc path_doc := ./doc
combine=${path_tools}/combine combine=${path_tools}/combine

View File

@ -15,8 +15,6 @@ path_manual_texi=${path_doc}/manual.texi
path_info_install := /usr/local/share/info path_info_install := /usr/local/share/info
src_js := index.js $(wildcard $(path_lib)/*.js)
src_tests := index.js $(wildcard $(path_test)/test-*)
doc_src := $(wildcard $(path_doc)/*.texi) doc_src := $(wildcard $(path_doc)/*.texi)
doc_imgs := $(patsubst %.dia, %.png, $(wildcard $(path_doc_img)/*.dia)) doc_imgs := $(patsubst %.dia, %.png, $(wildcard $(path_doc_img)/*.dia))
doc_imgs_txt := $(patsubst %.dia, %.png, $(wildcard $(path_doc_img)/*.txt)) doc_imgs_txt := $(patsubst %.dia, %.png, $(wildcard $(path_doc_img)/*.txt))

View File

@ -541,7 +541,7 @@ function buildMembers(
var dest = ( keywordStatic( keywords ) ) ? smethods : members; var dest = ( keywordStatic( keywords ) ) ? smethods : members;
member_builder.buildGetter( member_builder.buildGetter(
dest, null, name, value, keywords dest, null, name, value, keywords, base
); );
}, },
@ -550,7 +550,7 @@ function buildMembers(
var dest = ( keywordStatic( keywords ) ) ? smethods : members; var dest = ( keywordStatic( keywords ) ) ? smethods : members;
member_builder.buildSetter( member_builder.buildSetter(
dest, null, name, value, keywords dest, null, name, value, keywords, base
); );
}, },

View File

@ -154,6 +154,14 @@ function validateMethod( keywords, prev_data, value, name )
); );
} }
// do not allow overriding getters/setters
if ( prev_data && ( prev_data.get || prev_data.set ) )
{
throw TypeError(
"Cannot override getter/setter '" + name + "' with method"
);
}
// search for any previous instances of this member // search for any previous instances of this member
if ( prev ) if ( prev )
{ {
@ -236,6 +244,14 @@ exports.buildProp = function( members, meta, name, value, keywords, base )
); );
} }
// do not allow overriding getters/setters
if ( prev_data && ( prev_data.get || prev_data.set ) )
{
throw TypeError(
"Cannot override getter/setter '" + name + "' with property"
);
}
// do not permit visibility de-escalation // do not permit visibility de-escalation
if ( prev && ( prev_data.visibility < getVisibilityValue( keywords ) ) ) if ( prev && ( prev_data.visibility < getVisibilityValue( keywords ) ) )
{ {
@ -282,10 +298,14 @@ exports.buildProp = function( members, meta, name, value, keywords, base )
* *
* @param {Object.<string,boolean>} keywords parsed keywords * @param {Object.<string,boolean>} keywords parsed keywords
* *
* @param {Object=} base optional base object to scan
*
* @return {undefined} * @return {undefined}
*/ */
exports.buildGetter = function( members, meta, name, value, keywords ) exports.buildGetter = function( members, meta, name, value, keywords, base )
{ {
validateGetterSetter( members, keywords, name, base );
Object.defineProperty( Object.defineProperty(
getMemberVisibility( members, keywords ), getMemberVisibility( members, keywords ),
name, name,
@ -312,10 +332,14 @@ exports.buildGetter = function( members, meta, name, value, keywords )
* *
* @param {Object.<string,boolean>} keywords parsed keywords * @param {Object.<string,boolean>} keywords parsed keywords
* *
* @param {Object=} base optional base object to scan
*
* @return {undefined} * @return {undefined}
*/ */
exports.buildSetter = function( members, meta, name, value, keywords ) exports.buildSetter = function( members, meta, name, value, keywords, base )
{ {
validateGetterSetter( members, keywords, name, base );
Object.defineProperty( Object.defineProperty(
getMemberVisibility( members, keywords ), getMemberVisibility( members, keywords ),
name, name,
@ -330,6 +354,43 @@ exports.buildSetter = function( members, meta, name, value, keywords )
}; };
/**
* Performs common validations on getters/setters
*
* If a problem is found, an exception will be thrown.
*
* @param {{public: Object, protected: Object, private: Object}} members
*
* @param {Object.<string,boolean>} keywords parsed keywords
* @param {string} name getter/setter name
* @param {Object} base optional base to parse
*/
function validateGetterSetter( members, keywords, name, base )
{
var prev_data = scanMembers( members, name, base ),
prev = ( prev_data ) ? prev_data.member : null,
prev_keywords = ( prev && prev.___$$keywords$$ )
? prev.___$$keywords$$
: {}
;
if ( prev )
{
// To speed up the system we'll simply check for a getter/setter, rather
// than checking separately for methods/properties. This is at the
// expense of more detailed error messages. They'll live.
if ( !( prev_data.get || prev_data.set ) )
{
throw TypeError(
"Cannot override method or property '" + name +
"' with getter/setter"
);
}
}
}
/** /**
* Returns member prototype to use for the requested visibility * Returns member prototype to use for the requested visibility
* *
@ -389,18 +450,20 @@ function scanMembers( members, name, base )
// locate requested member by scanning each level of visibility // locate requested member by scanning each level of visibility
while ( i-- ) while ( i-- )
{ {
if ( member = members[ visibility[ i ] ][ name ] ) var visobj = members[ visibility[ i ] ];
// In order to support getters/setters, we must go off of the
// descriptor. We must also ignore base properties (last argument), such
// as Object.prototype.toString(). However, we must still traverse the
// prototype chain.
if ( member = util.getPropertyDescriptor( visobj, name, true ) )
{ {
// We need to filter out base properties (such as return {
// Object.prototype.toString()), but we still need to traverse the get: member.get,
// prototype chain. As such, we cannot use hasOwnProperty(). set: member.set,
if ( member !== Object.prototype[ name ] ) member: member.value,
{ visibility: ( ( fallback ) ? 0 : i ),
return { };
member: member,
visibility: ( ( fallback ) ? 0 : i ),
};
}
} }
} }

View File

@ -419,6 +419,95 @@ exports.arrayShrink = function( items )
}; };
/**
* Uses Object.getOwnPropertyDescriptor if available, otherwise provides our own
* implementation to fall back on
*
* If the environment does not support retrieving property descriptors (ES5),
* then the following will be true:
* - get/set will always be undefined
* - writable, enumerable and configurable will always be true
* - value will be the value of the requested property on the given object
*
* @param {Object} obj object to check property on
* @param {string} prop property to retrieve descriptor for
*
* @return {Object} descriptor for requested property or undefined if not found
*/
exports.getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
|| function( obj, prop )
{
if ( !Object.prototype.hasOwnProperty.call( obj, prop ) )
{
return undefined;
}
// fallback response
return {
get: undefined,
set: undefined,
writable: true,
enumerable: true,
configurable: true,
value: obj[ prop ],
};
};
/**
* Travels down the prototype chain of the given object in search of the
* requested property and returns its descriptor
*
* This operates as Object.getOwnPropertyDescriptor(), except that it traverses
* the prototype chain. For environments that do not support __proto__, it will
* not traverse the prototype chain and essentially serve as an alias for
* getOwnPropertyDescriptor().
*
* This method has the option to ignore the base prototype. This is useful to,
* for example, not catch properties like Object.prototype.toString() when
* searching for 'toString' on an object.
*
* @param {Object} obj object to check property on
* @param {string} prop property to retrieve descriptor for
* @param {bool} nobase whether to ignore the base prototype
*
* @return {Object} descriptor for requested property or undefined if not found
*/
exports.getPropertyDescriptor = function( obj, prop, nobase )
{
// false by default
nobase = !!nobase;
// note that this uses util's function, not Object's
var desc = exports.getOwnPropertyDescriptor( obj, prop ),
next = obj.__proto__;
// if we didn't find a descriptor and a prototype is available, recurse down
// the prototype chain, ensuring that the next prototype has a prototype if
// the base is to be excluded
if ( !desc && next && ( !nobase || next.__proto__ ) )
{
return exports.getPropertyDescriptor( obj.__proto__, prop, nobase );
}
// return the descriptor or undefined if no prototype is available
return desc;
};
/**
* Indicates whether or not the getPropertyDescriptor method is capable of
* traversing the prototype chain
*
* @type {boolean}
*/
exports.defineSecureProp( exports.getPropertyDescriptor, 'canTraverse',
( {}.__proto__ ) ? true : false
);
/** /**
* Appropriately returns defineSecureProp implementation to avoid check on each * Appropriately returns defineSecureProp implementation to avoid check on each
* invocation * invocation

168
lib/warn.js 100644
View File

@ -0,0 +1,168 @@
/**
* ease.js warning system
*
* Copyright (C) 2010 Mike Gerwitz
*
* This file is part of ease.js.
*
* ease.js is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Mike Gerwitz
* @package core
*/
/**
* Active warning handler
* @type {function()}
*/
var _handler = null;
/**
* Permits wrapping an exception as a warning
*
* Warnings are handled differently by the system, depending on the warning
* level that has been set.
*
* @param {Error} e exception (error) to wrap
*
* @return {Warning} new warning instance
*/
var Warning = exports.Warning = function( e )
{
// allow instantiation without use of 'new' keyword
if ( !( this instanceof Warning ) )
{
return new Warning( e );
}
// ensure we're wrapping an exception
if ( !( e instanceof Error ) )
{
throw TypeError( "Must provide exception to wrap" );
}
// copy over the message for convenience
this.message = e.message;
this._error = e;
};
Warning.prototype = Error();
/**
* Return the error wrapped by the warning
*
* @return {Error} wrapped error
*/
Warning.prototype.getError = function()
{
return this._error;
};
/**
* Core warning handlers
* @type {Object}
*/
exports.handlers = {
/**
* Logs message to console
*
* Will attempt to log using console.warn(), falling back to console.log()
* if necessary and aborting entirely if neither is available.
*
* This is useful as a default option to bring problems to the developer's
* attention without affecting the control flow of the software.
*
* @param {Warning} warning to log
*
* @return {undefined}
*/
log: function( warning )
{
var dest;
console && ( dest = console.warn || console.log ) &&
dest( warning.message );
},
/**
* Throws the error associated with the warning
*
* This handler is useful for development and will ensure that problems are
* brought to the attention of the developer.
*
* @param {Warning} warning to log
*
* @return {undefined}
*/
throwError: function( warning )
{
throw warning.getError();
},
/**
* Ignores warnings
*
* This is useful in a production environment where (a) warnings will affect
* the reputation of the software or (b) warnings may provide too much
* insight into the software. If using this option, you should always
* develop in a separate environment so that the system may bring warnings
* to your attention.
*
* @param {Warning} warning to log
*
* @return {undefined}
*/
dismiss: function( warning )
{
// do nothing
},
};
/**
* Sets the active warning handler
*
* You may use any of the predefined warning handlers or pass your own function.
*
* @param {function( Warning )} handler warning handler
*
* @return {undefined}
*/
exports.setHandler = function( handler )
{
_handler = handler;
};
/**
* Handles a warning using the active warning handler
*
* @param {Warning} warning warning to handle
*
* @return {undefined}
*/
exports.handle = function( warning )
{
_handler( warning );
}
// set the default handler
_handler = exports.handlers.log;

View File

@ -56,15 +56,41 @@ function setUp()
/** /**
* Partially applied function to quickly build getter from common test data * Partially applied function to quickly build getter from common test data
*/ */
function buildGetterSetterQuick( keywords, val ) function buildGetterSetterQuick( keywords, val, preserve_prior, use )
{ {
preserve_prior = !!preserve_prior;
use = ( use === undefined ) ? 0 : +use;
keywords = keywords || {}; keywords = keywords || {};
val = val || value; val = val || value;
setUp(); if ( !preserve_prior )
{
setUp();
}
buildGetter( members, meta, name, val, keywords ); if ( use == 0 || use == 1 )
buildSetter( members, meta, name, val, keywords ); {
buildGetter( members, meta, name, val, keywords );
}
if ( use == 0 || use == 2 )
{
buildSetter( members, meta, name, val, keywords );
}
}
function testEach( test )
{
test( 'getter', function( keywords, val, preserve )
{
buildGetterSetterQuick.call( this, keywords, val, preserve, 1 );
} );
test( 'setter', function( keywords, val, preserve )
{
buildGetterSetterQuick.call( this, keywords, val, preserve, 2 );
} );
} }
@ -176,3 +202,62 @@ function assertOnlyVisibility( vis, name, value, message )
} )(); } )();
/**
* Getters/setters should not be able to override methods, for the obvious
* reason that they are two different types and operate entirely differently. Go
* figure.
*/
testEach( function testCannotOverrideMethodWithGetterOrSetter( type, build )
{
setUp();
// method
members[ 'public' ][ name ] = function() {};
try
{
// attempt to override method with getter/setter (should fail)
build( { 'public': true }, null, true );
}
catch ( e )
{
assert.ok( e.message.search( name ) !== -1,
"Method override error message should contain getter/setter name"
);
return;
}
assert.fail( type + " should not be able to override methods");
} );
/**
* Getters/setters should not be able to override properties. While, at first,
* this concept may seem odd, keep in mind that the parent would likely not
* expect a subtype to be able to override property assignments. This could open
* up holes to exploit the parent class.
*/
testEach( function testCannotOverridePropertiesWithGetterOrSetter( type, build )
{
setUp();
// declare a property
members[ 'public' ][ name ] = 'foo';
try
{
// attempt to override property with getter/setter (should fail)
build( { 'public': true }, null, true );
}
catch ( e )
{
assert.ok( e.message.search( name ) !== -1,
"Property override error message should contain getter/setter name"
);
return;
}
assert.fail( type + " should not be able to override properties" );
} );

View File

@ -25,7 +25,8 @@
var common = require( './common' ), var common = require( './common' ),
assert = require( 'assert' ), assert = require( 'assert' ),
mb_common = require( __dirname + '/inc-member_builder-common' ), mb_common = require( __dirname + '/inc-member_builder-common' ),
builder = common.require( 'member_builder' ) builder = common.require( 'member_builder' ),
util = common.require( 'util' )
; ;
mb_common.funcVal = 'foobar'; mb_common.funcVal = 'foobar';
@ -440,3 +441,85 @@ mb_common.assertCommon();
}, TypeError, "Cannot declare private abstract method" ); }, TypeError, "Cannot declare private abstract method" );
} )(); } )();
/**
* While getters are technically methods, it doesn't make sense to override
* getters/setters with methods because they are fundamentally different.
*/
( function testCannotOverrideGetters()
{
if ( util.definePropertyFallback() )
{
return;
}
mb_common.members[ 'public' ] = {};
Object.defineProperty( mb_common.members[ 'public' ], mb_common.name, {
get: function() {},
} );
try
{
mb_common.value = function() {};
mb_common.buildMemberQuick( {}, true );
}
catch ( e )
{
assert.ok( e.message.search( mb_common.name ) !== -1,
"Method override getter failure should contain method name"
);
// ensure we have the correct error
assert.ok( e.message.search( 'getter' ) !== -1,
"Proper error is thrown for getter override failure"
);
return;
}
assert.fail(
"Should not be permitted to override getters with methods"
);
} )();
/**
* While setters are technically methods, it doesn't make sense to override
* getters/setters with methods because they are fundamentally different.
*/
( function testCannotOverrideSetters()
{
if ( util.definePropertyFallback() )
{
return;
}
mb_common.members[ 'public' ] = {};
Object.defineProperty( mb_common.members[ 'public' ], mb_common.name, {
set: function() {},
} );
try
{
mb_common.value = function() {};
mb_common.buildMemberQuick( {}, true );
}
catch ( e )
{
assert.ok( e.message.search( mb_common.name ) !== -1,
"Method override setter failure should contain method name"
);
// ensure we have the correct error
assert.ok( e.message.search( 'setter' ) !== -1,
"Proper error is thrown for setter override failure"
);
return;
}
assert.fail(
"Should not be permitted to override setters with methods"
);
} )();

View File

@ -25,7 +25,8 @@
var common = require( './common' ), var common = require( './common' ),
assert = require( 'assert' ), assert = require( 'assert' ),
mb_common = require( __dirname + '/inc-member_builder-common' ), mb_common = require( __dirname + '/inc-member_builder-common' ),
builder = common.require( 'member_builder' ) builder = common.require( 'member_builder' ),
util = common.require( 'util' )
; ;
@ -92,3 +93,85 @@ mb_common.assertCommon();
assert.fail( "Should not be permitted to declare virtual properties" ); assert.fail( "Should not be permitted to declare virtual properties" );
} )(); } )();
/*
* While getters act as properties, it doesn't make sense to override
* getters/setters with properties because they are fundamentally different.
*/
( function testCannotOverrideGetters()
{
if ( util.definePropertyFallback() )
{
return;
}
mb_common.members[ 'public' ] = {};
Object.defineProperty( mb_common.members[ 'public' ], mb_common.name, {
get: function() {},
} );
try
{
mb_common.value = 'foo';
mb_common.buildMemberQuick( {}, true );
}
catch ( e )
{
assert.ok( e.message.search( mb_common.name ) !== -1,
"Property override getter failure should contain property name"
);
// ensure we have the correct error
assert.ok( e.message.search( 'getter' ) !== -1,
"Proper error is thrown for getter override failure"
);
return;
}
assert.fail(
"Should not be permitted to override getters with properties"
);
} )();
/**
* While setters act as properties, it doesn't make sense to override
* getters/setters with properties because they are fundamentally different.
*/
( function testCannotOverrideSetters()
{
if ( util.definePropertyFallback() )
{
return;
}
mb_common.members[ 'public' ] = {};
Object.defineProperty( mb_common.members[ 'public' ], mb_common.name, {
set: function() {},
} );
try
{
mb_common.value = 'foo';
mb_common.buildMemberQuick( {}, true );
}
catch ( e )
{
assert.ok( e.message.search( mb_common.name ) !== -1,
"Property override setter failure should contain method name"
);
// ensure we have the correct error
assert.ok( e.message.search( 'setter' ) !== -1,
"Proper error is thrown for setter override failure"
);
return;
}
assert.fail(
"Should not be permitted to override setters with properties"
);
} )();

View File

@ -0,0 +1,137 @@
/**
* Tests util.getPropertyDescriptor
*
* Copyright (C) 2010 Mike Gerwitz
*
* This file is part of ease.js.
*
* ease.js is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Mike Gerwitz
* @package test
*/
var common = require( './common' ),
assert = require( 'assert' ),
util = common.require( 'util' ),
get_set = !( util.definePropertyFallback() )
;
/**
* If Object.getOwnPropertyDescriptor is provided by our environment, it should
* be used by util
*/
( function testUtilGetOwnPropertyDescriptorIsObjectsIfAvailable()
{
if ( Object.getOwnPropertyDescriptor )
{
assert.strictEqual(
util.getOwnPropertyDescriptor,
Object.getOwnPropertyDescriptor,
"Util should use Object.getOwnPropertyDescriptor if available"
);
}
} )();
/**
* The function should provide a boolean value indicating whether it can
* traverse the prototype chain
*/
( function testIndicatesWhetherTraversalIsPossible()
{
var traversable = ( {}.__proto__ ) ? true : false;
assert.equal( util.getPropertyDescriptor.canTraverse, traversable,
"Indicates whether traversal is possible"
);
} )();
/**
* We don't want tricksters to get funky with our system
*/
( function testTraversablePropertyIsNonWritable()
{
var getDesc;
if ( get_set )
{
assert.equal(
Object.getOwnPropertyDescriptor(
util.getPropertyDescriptor, 'canTraverse'
).writable,
false,
"Should not be able to alter canTravese value"
);
}
} )();
/**
* The return value should mimic Object.getOwnPropertyDescriptor() if we're not
* having to traverse the prototype chain
*/
( function testActsExactlyAsGetOwnPropertyDescriptorInEs5SystemsOnSameObject()
{
var obj = { foo: 'bar' },
desc1 = util.getOwnPropertyDescriptor( obj, 'foo' ),
desc2 = util.getPropertyDescriptor( obj, 'foo' )
;
assert.deepEqual( desc1, desc2,
"When operating one level deep, should return same as " +
"Object.getOwnPropertyDescriptor"
);
} )();
/**
* If we *do* have to start traversing the prototype chain (which
* Object.getOwnPropertyDescriptor() cannot do), then it should be as if we
* called Object.getOwnPropertyDescriptor() on the object in the prototype chain
* containing the requested property.
*/
( function testTraversesThePrototypeChain()
{
// if we cannot traverse the prototype chain, this test is pointless
if ( !util.getPropertyDescriptor.canTraverse )
{
return;
}
var proto = { foo: 'bar' },
obj = function() {}
;
obj.prototype = proto;
// to give ourselves the prototype chain (we don't want to set __proto__
// because this test will also be run on pre-ES5 engines)
var inst = new obj(),
// get the actual descriptor
expected = util.getOwnPropertyDescriptor( proto, 'foo' ),
// attempt to gather the descriptor from the prototype chain
given = util.getPropertyDescriptor( inst, 'foo' )
;
assert.deepEqual( given, expected,
"Properly traverses the prototype chain to retrieve the descriptor"
);
} )();

View File

@ -0,0 +1,101 @@
/**
* Tests the Warning prototype
*
* Copyright (C) 2010 Mike Gerwitz
*
* This file is part of ease.js.
*
* ease.js is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Mike Gerwitz
* @package test
*/
var common = require( './common' ),
assert = require( 'assert' ),
Warning = common.require( 'warn' ).Warning
;
/**
* Warning's prototype should be Error to ensure instanceof() checks work
* properly
*/
( function testWarningIsAvailableAndHasErrorAsPrototype()
{
assert.ok( ( Warning.prototype instanceof Error ),
"Warning should be an instance of Error"
);
} )();
/**
* Just as with the other Error classes, as well as all ease.js classes, the
* 'new' keyword should be optional when instantiating the class
*/
( function testNewKeywordIsNotRequiredForInstantiation()
{
assert.ok( Warning( Error( '' ) ) instanceof Warning,
"'new' keyword should not be necessary to instantiate Warning"
);
} )();
/**
* Warning message should be taken from the exception passed to it
*/
( function testCanWarningMessageIsSetFromWrappedException()
{
var err = Error( 'oshit' ),
warning = Warning( err );
assert.equal( warning.message, err.message,
"Warning message should be taken from wrapped exception"
);
} )();
/**
* The whole point of Warning is to wrap an exception. So, ensure that one is
* wrapped.
*/
( function testThrowsExceptionIfNoExceptionIsWrapped()
{
assert.throws( function()
{
Warning( /* nothing provided to wrap */ );
}, TypeError, "Exception should be thrown if no exception is provided" );
assert.throws( function()
{
Warning( 'not an exception' );
}, TypeError, "Exception should be thrown if given value is not an Error" );
} )();
/**
* We must provide access to the wrapped exception so that it can be properly
* handled. Warning is only intended to provide additional information so that
* ease.js may handle it differently than other Error instances.
*/
( function testCanRetrieveWrappedException()
{
var err = Error( 'foo' ),
warning = Warning( err );
assert.deepEqual( err, warning.getError(),
"Can retrieve wrapped exception"
);
} )();

View File

@ -0,0 +1,179 @@
/**
* Tests core warning handlers
*
* Copyright (C) 2010 Mike Gerwitz
*
* This file is part of ease.js.
*
* ease.js is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Mike Gerwitz
* @package test
*/
var common = require( './common' ),
assert = require( 'assert' ),
warn = common.require( 'warn' ),
Warning = warn.Warning,
warning = Warning( Error( 'gninraw' ) )
;
/**
* Return the console object, without throwing errors if it does not exist
*
* @return {Object} console
*/
function backupConsole()
{
// ensure that we don't throw errors if console is not defined
if ( typeof console !== 'undefined' )
{
return console;
}
return undefined;
}
/**
* The log warning handler should log warnings to the console
*/
( function testLogWarningHandlerLogsMessageToConsole()
{
var logged = false,
// back up console ref
console_ = backupConsole()
;
// mock console
console = {
warn: function( message )
{
assert.equal( message, warning.message,
"Should log proper message to console"
);
logged = true;
},
};
// call handler with the warning
warn.handlers.log( warning );
assert.equal( logged, true,
"Message should be logged to console"
);
// restore console
console = console_;
} )();
/**
* Some environments may not have a console reference, or they may not have
* console.warn. In this case, we just want to make sure we don't throw an error
* when attempting to invoke undefined, or access a property of undefined.
*/
( function testLogWarningHandlerHandlesMissingConsole()
{
// back up console
var console_ = backupConsole();
// destroy it
console = undefined;
// attempt to log
warn.handlers.log( warning );
// restore console
console = console_;
} )();
/**
* Furthermore, an environment may implement console.log(), but not
* console.warn(). By default, we use warn(), so let's ensure we can fall back
* to log() if warn() is unavailable.
*/
( function testLogWarningHandlerWillFallBackToLogMethodIfWarnIsMissing()
{
// back up and overwrite console to contain only log()
var console_ = backupConsole(),
given = '';
console = {
log: function( message )
{
given = message;
}
};
// attempt to log
warn.handlers.log( warning );
assert.equal( given, warning.message,
"Should fall back to log() and log proper message"
);
// restore console
console = console_;
} )();
/**
* The throwError warning handler should throw the wrapped error as an exception
*/
( function testThrowErrorWarningHandlerThrowsWrappedError()
{
try
{
warn.handlers.throwError( warning );
}
catch ( e )
{
assert.deepEqual( e, warning.getError(),
"Wrapped exception should be thrown"
);
return;
}
assert.fail( "Wrapped exception should be thrown" );
} )();
/**
* The 'dismiss' error handler is a pretty basic concept. Simply do nothing. We
* don't want to log, we don't want to throw anything, we just want to pretend
* nothing ever happened and move on our merry way. This is intended for use in
* production environments where providing warnings may provide too much insight
* into the software.
*/
( function testDismissWarningHandlerShouldDoNothing()
{
// destroy the console to ensure nothing is logged
var console_ = backupConsole();
console = undefined;
// don't catch anything, to ensure no errors occur and that no exceptions
// are thrown
warn.handlers.dismiss( warning );
// restore console
console = console_;
} )();

View File

@ -0,0 +1,83 @@
/**
* Tests warning system implementation
*
* Copyright (C) 2010 Mike Gerwitz
*
* This file is part of ease.js.
*
* ease.js is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Mike Gerwitz
* @package test
*/
var common = require( './common' ),
assert = require( 'assert' ),
warn = common.require( 'warn' )
;
/**
* The default warning handler should be the 'log' handler. This is a friendly
* compromise that will allow the developer to be warned of potential issues
* without affecting program execution.
*/
( function testDefaultHandlerIsLogger()
{
// back up console object
var console_ = ( typeof console !== 'undefined' ) ? console : undefined,
called = false;
// stub it
console = {
warn: function()
{
called = true;
},
};
warn.handle( warn.Warning( Error( 'foo' ) ) );
assert.ok( called,
"Default handler will log to console"
);
// restore console
console = console_;
} )();
/**
* The warning handler can be altered at runtime. Ensure we can set it and call
* it appropriately. We do not need to use one of the pre-defined handlers.
*/
( function testCanSetAndCallWarningHandler()
{
var given,
warning = warn.Warning( Error( 'foo' ) );
// set a stub warning handler
warn.setHandler( function( warn )
{
given = warn;
} );
// trigger the handler
warn.handle( warning );
assert.deepEqual( given, warning,
"Set warning handler should be called with given Warning"
);
} )();

View File

@ -29,7 +29,7 @@ TPL_VAR='/**{CONTENT}**/'
RMTRAIL="$PATH_TOOLS/rmtrail" RMTRAIL="$PATH_TOOLS/rmtrail"
# order matters # order matters
CAT_MODULES="prop_parser util propobj member_builder class_builder" CAT_MODULES="warn prop_parser util propobj member_builder class_builder"
CAT_MODULES="$CAT_MODULES class class_final class_abstract interface" CAT_MODULES="$CAT_MODULES class class_final class_abstract interface"
## ##

View File

@ -73,7 +73,7 @@ module.assert = { exports: {
return; return;
} }
if ( cmp instanceof Array ) if ( ( cmp instanceof Array ) && ( val instanceof Array ) )
{ {
var i = 0, var i = 0,
len = cmp.length; len = cmp.length;
@ -86,7 +86,7 @@ module.assert = { exports: {
return; return;
} }
else if ( cmp instanceof Object ) else if ( ( typeof cmp === 'object' ) && ( typeof val === 'object' ) )
{ {
for ( var i in cmp ) for ( var i in cmp )
{ {