fudge-1.0.3/000755 000765 000024 00000000000 11536723452 013077 5ustar00kumarstaff000000 000000 fudge-1.0.3/docs/000755 000765 000024 00000000000 11536723452 014027 5ustar00kumarstaff000000 000000 fudge-1.0.3/fudge/000755 000765 000024 00000000000 11536723452 014171 5ustar00kumarstaff000000 000000 fudge-1.0.3/fudge.egg-info/000755 000765 000024 00000000000 11536723452 015663 5ustar00kumarstaff000000 000000 fudge-1.0.3/javascript/000755 000765 000024 00000000000 11536723452 015245 5ustar00kumarstaff000000 000000 fudge-1.0.3/LICENSE.txt000644 000765 000024 00000002064 11456430705 014721 0ustar00kumarstaff000000 000000 The MIT License Copyright (c) 2009 Kumar McMillan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fudge-1.0.3/._MANIFEST.in000644 000765 000024 00000000271 11456430705 015047 0ustar00kumarstaff000000 000000 Mac OS X  2‡¹ATTR芹˜!˜!com.macromates.caret{ column = 2; line = 3; }fudge-1.0.3/MANIFEST.in000644 000765 000024 00000000341 11456430705 014630 0ustar00kumarstaff000000 000000 include *.txt recursive-include javascript/fudge *.js *.html *.txt *.py include run_tests.sh include javascript/README.txt include docs/Makefile recursive-include docs *.py *.rst include docs/_static/.hidden prune docs/_buildfudge-1.0.3/PKG-INFO000644 000765 000024 00000004072 11536723452 014177 0ustar00kumarstaff000000 000000 Metadata-Version: 1.0 Name: fudge Version: 1.0.3 Summary: Replace real objects with fakes (mocks, stubs, etc) while testing. Home-page: http://farmdev.com/projects/fudge/ Author: Kumar McMillan Author-email: kumar.mcmillan@gmail.com License: The MIT License Description: Complete documentation is available at http://farmdev.com/projects/fudge/ Fudge is a Python module for using fake objects (mocks and stubs) to test real ones. In readable Python code, you declare what methods are available on your fake and how they should be called. Then you inject that into your application and start testing. This declarative approach means you don't have to record and playback actions and you don't have to inspect your fakes after running code. If the fake object was used incorrectly then you'll see an informative exception message with a traceback that points to the culprit. Here is a quick preview of how you can test code that sends email without actually sending email:: @fudge.patch('smtplib.SMTP') def test_mailer(FakeSMTP): # Declare how the SMTP class should be used: (FakeSMTP.expects_call() .expects('connect') .expects('sendmail').with_arg_count(3)) # Run production code: send_mail() # ...expectations are verified automatically at the end of the test Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.1 Classifier: Programming Language :: Python :: 3.2 Classifier: Topic :: Software Development :: Testing fudge-1.0.3/._README.txt000644 000765 000024 00000000273 11464552421 015010 0ustar00kumarstaff000000 000000 Mac OS X  2‰»ATTRè§»˜#˜#com.macromates.caret{ column = 12; line = 16; }fudge-1.0.3/README.txt000644 000765 000024 00000001011 11464552421 014562 0ustar00kumarstaff000000 000000 Fudge is a module for replacing real objects with fakes (mocks, stubs, etc) while testing. Documentation is available at http://farmdev.com/projects/fudge/ or else, you can build it from source like this:: $ easy_install Sphinx $ cd docs $ make html then open _build/html/index.html in your web browser. To run tests, you can use tox for all supported versions of Python. You can install it with pip:: $ pip install tox Then execute:: $ ./run_tests.sh Or simply:: $ tox fudge-1.0.3/._run_tests.sh000755 000765 000024 00000000272 11464552003 015672 0ustar00kumarstaff000000 000000 Mac OS X  2ˆºATTR,Gº˜"˜"com.macromates.caret{ column = 43; line = 5; }fudge-1.0.3/run_tests.sh000755 000765 000024 00000000202 11464552003 015446 0ustar00kumarstaff000000 000000 #!/bin/sh if [ -e "`which tox`" ]; then tox $@ else echo "**** install tox to run the tests http://codespeak.net/tox/" fifudge-1.0.3/setup.cfg000644 000765 000024 00000000073 11536723452 014720 0ustar00kumarstaff000000 000000 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 fudge-1.0.3/._setup.py000644 000765 000024 00000000273 11532310334 015013 0ustar00kumarstaff000000 000000 Mac OS X  2‰»ATTRè廘#˜#com.macromates.caret{ column = 27; line = 43; }fudge-1.0.3/setup.py000644 000765 000024 00000004660 11532310334 014602 0ustar00kumarstaff000000 000000 import re import sys from setuptools import setup, find_packages version = None for line in open("./fudge/__init__.py"): m = re.search("__version__\s*=\s*(.*)", line) if m: version = m.group(1).strip()[1:-1] # quotes break assert version extra_setup = {} if sys.version_info >= (3,): extra_setup['use_2to3'] = True # extra_setup['use_2to3_fixers'] = ['your.fixers'] setup( name='fudge', version=version, description="Replace real objects with fakes (mocks, stubs, etc) while testing.", long_description=""" Complete documentation is available at http://farmdev.com/projects/fudge/ Fudge is a Python module for using fake objects (mocks and stubs) to test real ones. In readable Python code, you declare what methods are available on your fake and how they should be called. Then you inject that into your application and start testing. This declarative approach means you don't have to record and playback actions and you don't have to inspect your fakes after running code. If the fake object was used incorrectly then you'll see an informative exception message with a traceback that points to the culprit. Here is a quick preview of how you can test code that sends email without actually sending email:: @fudge.patch('smtplib.SMTP') def test_mailer(FakeSMTP): # Declare how the SMTP class should be used: (FakeSMTP.expects_call() .expects('connect') .expects('sendmail').with_arg_count(3)) # Run production code: send_mail() # ...expectations are verified automatically at the end of the test """, author='Kumar McMillan', author_email='kumar.mcmillan@gmail.com', license="The MIT License", packages=find_packages(exclude=['ez_setup']), install_requires=[], url='http://farmdev.com/projects/fudge/', include_package_data=True, classifiers = [ 'Intended Audience :: Developers', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.1', 'Programming Language :: Python :: 3.2', 'Topic :: Software Development :: Testing' ], **extra_setup ) fudge-1.0.3/javascript/fudge/000755 000765 000024 00000000000 11536723452 016337 5ustar00kumarstaff000000 000000 fudge-1.0.3/javascript/README.txt000644 000765 000024 00000000727 11456430706 016747 0ustar00kumarstaff000000 000000 Copy ``javascript/fudge/`` to your webroot. To use it in your tests all you need is a script tag like this:: If you want to run Fudge's own tests, then cd into the ``javascript/`` directory, start a simple webserver:: $ python fudge/testserver.py and open http://localhost:8000/tests/test_fudge.html Take note that while Fudge's *tests* require jQuery, Fudge itself does not require jQuery.fudge-1.0.3/javascript/fudge/fudge.js000644 000765 000024 00000041653 11456430706 017776 0ustar00kumarstaff000000 000000 /** * Fudge is a JavaScript module for using fake objects (mocks, stubs, etc) to test real ones. *

* The module is designed for two specific situations: *

* * @module fudge */ fudge = function() { var AssertionError = function(msg) { Error.call(this, msg); this.name = "AssertionError"; this.message = msg; }; AssertionError.prototype.toString = function() { return this.message; }; var registry = new function() { this.expected_calls = []; this.clear_actual_calls = function() { /* def clear_actual_calls(self): for exp in self.get_this.expected_calls(): exp.was_called = False */ for (var i=0; i A fake object to replace a real one while testing.

All calls return ``this`` so that you can chain them together to create readable code.

Arguments:

Short example:


       var auth = new fudge.Fake('auth')
                                .expects('login')
                                .with_args('joe_username', 'joes_password');
       fudge.clear_calls();
       auth.login();
       fudge.verify();
       
* * @class Fake * @namespace fudge */ var Fake = function(name, config) { if (!config) { config = {}; } this._name = name; if (config.object) { this._object = config.object; } else { // object is a global by name if (name) { var parts = name.split("."); if (parts.length === 0) { // empty string? throw new Error("Fake('" + name + "'): invalid name"); } // descend into dot-separated object. // i.e. // foo.bar.baz // window[foo] // foo[bar] // baz var last_parent = window; for (var i=0; i
If the method *call_name* is never called, then raise an error. * @method expects * @return Object */ Fake.prototype.expects = function(call_name) { this._last_declared_call_name = call_name; var c = new ExpectedCall(this, call_name); this._declared_calls[call_name] = c; registry.expect_call(c); return this; }; /** * Provide a call.

The call acts as a stub -- no error is raised if it is not called. * * @method provides * @return Object */ Fake.prototype.provides = function(call_name) { /* def provides(self, call_name): """Provide a call.""" self._last_declared_call_name = call_name c = Call(self, call_name) self._declared_calls[call_name] = c return self */ }; /** * Set the last call to return a value.

Set a static value to return when a method is called. * * @method returns * @return Object */ Fake.prototype.returns = function(val) { var exp = this._get_current_call(); exp.return_val = val; return this; }; /** *

Set the last call to return a new :class:`fudge.Fake`.

Any given arguments are passed to the :class:`fudge.Fake` constructor

Take note that this is different from the cascading nature of other methods. This will return an instance of the *new* Fake, not self, so you should be careful to store its return value in a new variable.

* * @method returns_fake * @return Object */ Fake.prototype.returns_fake = function() { // make an anonymous object ... var return_val = {}; var fake = new Fake(this._name, { "object": return_val }); // ... then attach it to the return value of the last declared method this.returns(return_val); return fake; }; /** * Set the last call to expect specific arguments. * * @method with_args * @return Object */ Fake.prototype.with_args = function() { var exp = this._get_current_call(); exp.expected_arguments = arguments; return this; }; /** * Set the last call to expect an exact argument count. * * @method with_arg_count * @return Object */ Fake.prototype.with_arg_count = function(count) { /* def with_arg_count(self, count): """Expect an exact argument count.""" exp = self._get_current_call() exp.expected_arg_count = count return self */ }; /** * Set the last call to expect an exact count of keyword arguments. * * @method with_kwarg_count * @return Object */ Fake.prototype.with_kwarg_count = function(count) { /* def with_kwarg_count(self, count): """Expect an exact count of keyword arguments.""" exp = self._get_current_call() exp.expected_kwarg_count = count return self */ }; // fill fudge.* namespace : return { '__version__': '0.9.3', AssertionError: AssertionError, clear_expectations: function() { return registry.clear_expectations(); }, ExpectedCall: ExpectedCall, Fake: Fake, registry: registry, clear_calls: function() { return registry.clear_calls(); }, verify: function() { return registry.verify(); }, repr_call_args: repr_call_args }; }(); // end fudge namespace fudge-1.0.3/javascript/fudge/tests/000755 000765 000024 00000000000 11536723452 017501 5ustar00kumarstaff000000 000000 fudge-1.0.3/javascript/fudge/testserver.py000644 000765 000024 00000003332 11456430706 021116 0ustar00kumarstaff000000 000000 import optparse import logging, time, os from wsgiref import simple_server log = logging.getLogger(__name__) document_root = os.path.dirname(__file__) def fileapp(environ, start_response): path_info = environ['PATH_INFO'] if path_info.startswith('/'): path_info = path_info[1:] # make relative full_path = os.path.join(document_root, path_info) if full_path == '': full_path = '.' # must be working dir if path_info=="" or path_info.endswith('/') or os.path.isdir(full_path): # directory listing: out = ['") body = "".join( out ) else: f = open(full_path, 'r') body = f.read() # optimized for small files :) start_response('200 OK', [ ('Content-Type', 'text/html'), ('Content-Length', str(len(body)))]) return [body] def main(): p = optparse.OptionParser(usage="%prog") p.add_option("--port", help="Port to run server on. Default: %default", default=8090, type=int) (options, args) = p.parse_args() logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s] %(message)s') log.info("starting test server on port %s", options.port) httpd = simple_server.WSGIServer(('', options.port), simple_server.WSGIRequestHandler) httpd.set_app(fileapp) httpd.serve_forever() if __name__ == '__main__': main()fudge-1.0.3/javascript/fudge/tests/jquery/000755 000765 000024 00000000000 11536723452 021020 5ustar00kumarstaff000000 000000 fudge-1.0.3/javascript/fudge/tests/test_fudge.html000644 000765 000024 00000001002 11456430706 022507 0ustar00kumarstaff000000 000000

Test Fudge

    fudge-1.0.3/javascript/fudge/tests/test_fudge.js000644 000765 000024 00000017566 11456430706 022205 0ustar00kumarstaff000000 000000 if (typeof nosejs !== "undefined") { nosejs.requireResource("jquery-1.3.1.js"); nosejs.requireResource("jquery/qunit-testrunner.js"); nosejs.requireFile("../fudge.js"); } function raises(exception_name, func) { var caught = false; try { func(); } catch (e) { caught = true; equals(e.name, exception_name, e); } if (!caught) { throw new fudge.AssertionError("expected to catch " + exception_name); } } function init_test() { fudge.registry.clear_all(); } module("Test Fake"); test("Fake().toString", function() { equals(new fudge.Fake("something").toString(), "fake:something"); }); test("can find objects", function() { var fake; init_test(); simple = {iam: function() { return "simple"; }}; dot = {}; dot.sep = {iam: function() { return "dot.sep"; }}; fake = new fudge.Fake("simple"); equals(fake._object.iam(), "simple"); fake = new fudge.Fake("dot.sep"); equals(fake._object.iam(), "dot.sep"); }); // this test made more sense when Fake used _object = eval(name) /* test("cannot send bad JavaScript as name", function() { init_test(); expect(1); raises("TypeError", function() { var fake = new fudge.Fake("length()"); // TypeError: length is not a function }); }); */ test("can create objects", function() { init_test(); var fake = new fudge.Fake("_non_existant_.someCall"); ok(_non_existant_, "_non_existant_"); ok(_non_existant_.someCall, "_non_existant_.someCall"); }); test("expected call not called", function() { /* @raises(AssertionError) def test_nocall(self): exp = self.fake.expects('something') fudge.verify() */ init_test(); expect(1); var fake = new fudge.Fake("some_obj"); fake.expects("someCall"); raises("AssertionError", function() { fudge.verify(); }); }); test("call intercepted", function() { init_test(); var fake = new fudge.Fake("bob_loblaw"); fake.expects("blog"); bob_loblaw.blog(); // verify should pass the test... fudge.verify(); }); test("returns value", function() { init_test(); var fake = new fudge.Fake("grocery_store"); fake.expects("check_eggs").returns("eggs are good!"); equals(grocery_store.check_eggs(), "eggs are good!"); fudge.verify(); }); test("returns fake", function() { init_test(); expect(1); var fake = new fudge.Fake("ice_skates"); fake.expects("go").returns_fake().expects("not_called"); ice_skates.go(); raises("AssertionError", function() { fudge.verify(); }); }); test("returns fake creates calls", function() { init_test(); var fake = new fudge.Fake("ice_skates").expects("foo").returns_fake().expects("bar"); ice_skates.foo().bar(); }); test("returns fake maintains expectations", function() { init_test(); var fake = new fudge.Fake("ice_skates"); fake.expects("go").returns_fake().expects("show_off"); ice_skates.go().show_off(); fudge.verify(); }); test("expected arguments are set", function() { init_test(); fudge.clear_expectations(); var fake = new fudge.Fake("Session").expects("add").with_args("one", {"debug":false}); var call = fudge.registry.expected_calls[0]; equals(call.expected_arguments[0], "one"); equals(call.expected_arguments[1].debug, false); }); test("expected arguments raises error", function() { init_test(); fudge.clear_expectations(); fudge.clear_calls(); var fake = new fudge.Fake("session").expects("add").with_args("one", {"debug":false}); raises("AssertionError", function() { session.add(); }); }); test("expected arguments pass", function() { init_test(); fudge.clear_expectations(); fudge.clear_calls(); console.log("arg test"); var fake = new fudge.Fake("session").expects("add").with_args("one", {"debug":false}); session.add("one", {"debug":false}); }); module("Test ExpectedCall"); test("ExpectedCall properties", function() { init_test(); var fake = new fudge.Fake("my_auth_mod"); var ec = new fudge.ExpectedCall(fake, "logout"); equals(ec.call_name, "logout"); }); test("call is logged", function() { init_test(); var fake = new fudge.Fake("aa_some_obj"); var ec = new fudge.ExpectedCall(fake, "watchMe"); aa_some_obj.watchMe(); equals(ec.was_called, true, "aa_some_obj.watchMe() was not logged"); }); module("Test fudge.registry"); /* def setUp(self): self.fake = Fake() self.reg = fudge.registry # in case of error, clear out everything: self.reg.clear_all() */ test("expected call not called", function() { /* @raises(AssertionError) def test_expected_call_not_called(self): self.reg.clear_calls() self.reg.expect_call(ExpectedCall(self.fake, 'nothing')) self.reg.verify() */ init_test(); expect(1); var fake = new fudge.Fake("some_obj"); fudge.registry.expect_call(new fudge.ExpectedCall(fake, 'nothing')); raises("AssertionError", function() { fudge.registry.verify(); }); }); test("start resets calls", function() { /* def test_start_resets_calls(self): exp = ExpectedCall(self.fake, 'callMe') self.reg.expect_call(exp) exp() eq_(exp.was_called, True) self.reg.clear_calls() eq_(exp.was_called, False, "call was not reset by start()") */ init_test(); var fake = new fudge.Fake("yeah"); var exp = new fudge.ExpectedCall(fake, "sayYeah"); fudge.registry.expect_call(exp); yeah.sayYeah(); equals(exp.was_called, true, "call was never logged"); fudge.registry.clear_calls(); equals(exp.was_called, false, "call was not reset by clear_calls()"); }); test("verify resets calls", function() { init_test(); var fake = new fudge.Fake("reset_yeah"); var exp = new fudge.ExpectedCall(fake, "sayYeah"); fudge.registry.expect_call(exp); equals(fudge.registry.expected_calls.length, 1, "registry has too many calls"); reset_yeah.sayYeah(); equals(exp.was_called, true, "call was never logged"); fudge.registry.verify(); equals(exp.was_called, false, "call was not reset by verify()"); equals(fudge.registry.expected_calls.length, 1, "verify() should not reset expectations"); }); test("global verify", function() { init_test(); var fake = new fudge.Fake("gverify_yeah"); var exp = new fudge.ExpectedCall(fake, "sayYeah"); gverify_yeah.sayYeah(); fudge.registry.expect_call(exp); equals(exp.was_called, true, "call was never logged"); equals(fudge.registry.expected_calls.length, 1, "registry has wrong number of calls"); fudge.verify(); equals(exp.was_called, false, "call was not reset by verify()"); equals(fudge.registry.expected_calls.length, 1, "verify() should not reset expectations"); }); test("global clear expectations", function() { /* def test_global_clear_expectations(self): exp = ExpectedCall(self.fake, 'callMe') exp() self.reg.expect_call(exp) eq_(len(self.reg.get_expected_calls()), 1) fudge.clear_expectations() eq_(len(self.reg.get_expected_calls()), 0, "clear_expectations() should reset expectations") */ init_test(); var fake = new fudge.Fake("gclear_yeah"); var exp = new fudge.ExpectedCall(fake, "sayYeah"); gclear_yeah.sayYeah(); fudge.registry.expect_call(exp); equals(fudge.registry.expected_calls.length, 1, "registry has wrong number of calls"); fudge.clear_expectations(); equals(fudge.registry.expected_calls.length, 0, "clear_expectations() should reset expectations"); }); module("utilities"); test("reproduce call args", function() { equals(fudge.repr_call_args(new Array("yeah's", {debug:true, when:'now'})), "('yeah\\'s', {'debug': true, 'when': 'now'})"); }); fudge-1.0.3/javascript/fudge/tests/jquery/jquery-1.2.6.js000644 000765 000024 00000303544 11456430706 023346 0ustar00kumarstaff000000 000000 (function(){ /* * jQuery 1.2.6 - New Wave Javascript * * Copyright (c) 2008 John Resig (jquery.com) * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ * $Rev: 5685 $ */ // Map over jQuery in case of overwrite var _jQuery = window.jQuery, // Map over the $ in case of overwrite _$ = window.$; var jQuery = window.jQuery = window.$ = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context ); }; // A simple way to check for HTML strings or ID strings // (both of which we optimize for) var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/, // Is it a simple selector isSimple = /^.[^:#\[\.]*$/, // Will speed up references to undefined, and allows munging its name. undefined; jQuery.fn = jQuery.prototype = { init: function( selector, context ) { // Make sure that a selection was provided selector = selector || document; // Handle $(DOMElement) if ( selector.nodeType ) { this[0] = selector; this.length = 1; return this; } // Handle HTML strings if ( typeof selector == "string" ) { // Are we dealing with HTML string or an ID? var match = quickExpr.exec( selector ); // Verify a match, and that no context was specified for #id if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) selector = jQuery.clean( [ match[1] ], context ); // HANDLE: $("#id") else { var elem = document.getElementById( match[3] ); // Make sure an element was located if ( elem ){ // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id != match[3] ) return jQuery().find( selector ); // Otherwise, we inject the element directly into the jQuery object return jQuery( elem ); } selector = []; } // HANDLE: $(expr, [context]) // (which is just equivalent to: $(content).find(expr) } else return jQuery( context ).find( selector ); // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) return jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector ); return this.setArray(jQuery.makeArray(selector)); }, // The current version of jQuery being used jquery: "1.2.6", // The number of elements contained in the matched element set size: function() { return this.length; }, // The number of elements contained in the matched element set length: 0, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { return num == undefined ? // Return a 'clean' array jQuery.makeArray( this ) : // Return just the object this[ num ]; }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems ) { // Build a new jQuery matched element set var ret = jQuery( elems ); // Add the old object onto the stack (as a reference) ret.prevObject = this; // Return the newly-formed element set return ret; }, // Force the current matched set of elements to become // the specified array of elements (destroying the stack in the process) // You should use pushStack() in order to do this, but maintain the stack setArray: function( elems ) { // Resetting the length to 0, then using the native Array push // is a super-fast way to populate an object with array-like properties this.length = 0; Array.prototype.push.apply( this, elems ); return this; }, // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); }, // Determine the position of an element within // the matched set of elements index: function( elem ) { var ret = -1; // Locate the position of the desired element return jQuery.inArray( // If it receives a jQuery object, the first element is used elem && elem.jquery ? elem[0] : elem , this ); }, attr: function( name, value, type ) { var options = name; // Look for the case where we're accessing a style value if ( name.constructor == String ) if ( value === undefined ) return this[0] && jQuery[ type || "attr" ]( this[0], name ); else { options = {}; options[ name ] = value; } // Check to see if we're setting style values return this.each(function(i){ // Set all the styles for ( name in options ) jQuery.attr( type ? this.style : this, name, jQuery.prop( this, options[ name ], type, i, name ) ); }); }, css: function( key, value ) { // ignore negative width and height values if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) value = undefined; return this.attr( key, value, "curCSS" ); }, text: function( text ) { if ( typeof text != "object" && text != null ) return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); var ret = ""; jQuery.each( text || this, function(){ jQuery.each( this.childNodes, function(){ if ( this.nodeType != 8 ) ret += this.nodeType != 1 ? this.nodeValue : jQuery.fn.text( [ this ] ); }); }); return ret; }, wrapAll: function( html ) { if ( this[0] ) // The elements to wrap the target around jQuery( html, this[0].ownerDocument ) .clone() .insertBefore( this[0] ) .map(function(){ var elem = this; while ( elem.firstChild ) elem = elem.firstChild; return elem; }) .append(this); return this; }, wrapInner: function( html ) { return this.each(function(){ jQuery( this ).contents().wrapAll( html ); }); }, wrap: function( html ) { return this.each(function(){ jQuery( this ).wrapAll( html ); }); }, append: function() { return this.domManip(arguments, true, false, function(elem){ if (this.nodeType == 1) this.appendChild( elem ); }); }, prepend: function() { return this.domManip(arguments, true, true, function(elem){ if (this.nodeType == 1) this.insertBefore( elem, this.firstChild ); }); }, before: function() { return this.domManip(arguments, false, false, function(elem){ this.parentNode.insertBefore( elem, this ); }); }, after: function() { return this.domManip(arguments, false, true, function(elem){ this.parentNode.insertBefore( elem, this.nextSibling ); }); }, end: function() { return this.prevObject || jQuery( [] ); }, find: function( selector ) { var elems = jQuery.map(this, function(elem){ return jQuery.find( selector, elem ); }); return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ? jQuery.unique( elems ) : elems ); }, clone: function( events ) { // Do the clone var ret = this.map(function(){ if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) { // IE copies events bound via attachEvent when // using cloneNode. Calling detachEvent on the // clone will also remove the events from the orignal // In order to get around this, we use innerHTML. // Unfortunately, this means some modifications to // attributes in IE that are actually only stored // as properties will not be copied (such as the // the name attribute on an input). var clone = this.cloneNode(true), container = document.createElement("div"); container.appendChild(clone); return jQuery.clean([container.innerHTML])[0]; } else return this.cloneNode(true); }); // Need to set the expando to null on the cloned set if it exists // removeData doesn't work here, IE removes it from the original as well // this is primarily for IE but the data expando shouldn't be copied over in any browser var clone = ret.find("*").andSelf().each(function(){ if ( this[ expando ] != undefined ) this[ expando ] = null; }); // Copy the events from the original to the clone if ( events === true ) this.find("*").andSelf().each(function(i){ if (this.nodeType == 3) return; var events = jQuery.data( this, "events" ); for ( var type in events ) for ( var handler in events[ type ] ) jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data ); }); // Return the cloned set return ret; }, filter: function( selector ) { return this.pushStack( jQuery.isFunction( selector ) && jQuery.grep(this, function(elem, i){ return selector.call( elem, i ); }) || jQuery.multiFilter( selector, this ) ); }, not: function( selector ) { if ( selector.constructor == String ) // test special case where just one selector is passed in if ( isSimple.test( selector ) ) return this.pushStack( jQuery.multiFilter( selector, this, true ) ); else selector = jQuery.multiFilter( selector, this ); var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; return this.filter(function() { return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; }); }, add: function( selector ) { return this.pushStack( jQuery.unique( jQuery.merge( this.get(), typeof selector == 'string' ? jQuery( selector ) : jQuery.makeArray( selector ) ))); }, is: function( selector ) { return !!selector && jQuery.multiFilter( selector, this ).length > 0; }, hasClass: function( selector ) { return this.is( "." + selector ); }, val: function( value ) { if ( value == undefined ) { if ( this.length ) { var elem = this[0]; // We need to handle select boxes special if ( jQuery.nodeName( elem, "select" ) ) { var index = elem.selectedIndex, values = [], options = elem.options, one = elem.type == "select-one"; // Nothing was selected if ( index < 0 ) return null; // Loop through all the selected options for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { var option = options[ i ]; if ( option.selected ) { // Get the specifc value for the option value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value; // We don't need an array for one selects if ( one ) return value; // Multi-Selects return an array values.push( value ); } } return values; // Everything else, we just grab the value } else return (this[0].value || "").replace(/\r/g, ""); } return undefined; } if( value.constructor == Number ) value += ''; return this.each(function(){ if ( this.nodeType != 1 ) return; if ( value.constructor == Array && /radio|checkbox/.test( this.type ) ) this.checked = (jQuery.inArray(this.value, value) >= 0 || jQuery.inArray(this.name, value) >= 0); else if ( jQuery.nodeName( this, "select" ) ) { var values = jQuery.makeArray(value); jQuery( "option", this ).each(function(){ this.selected = (jQuery.inArray( this.value, values ) >= 0 || jQuery.inArray( this.text, values ) >= 0); }); if ( !values.length ) this.selectedIndex = -1; } else this.value = value; }); }, html: function( value ) { return value == undefined ? (this[0] ? this[0].innerHTML : null) : this.empty().append( value ); }, replaceWith: function( value ) { return this.after( value ).remove(); }, eq: function( i ) { return this.slice( i, i + 1 ); }, slice: function() { return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); }, map: function( callback ) { return this.pushStack( jQuery.map(this, function(elem, i){ return callback.call( elem, i, elem ); })); }, andSelf: function() { return this.add( this.prevObject ); }, data: function( key, value ){ var parts = key.split("."); parts[1] = parts[1] ? "." + parts[1] : ""; if ( value === undefined ) { var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); if ( data === undefined && this.length ) data = jQuery.data( this[0], key ); return data === undefined && parts[1] ? this.data( parts[0] ) : data; } else return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){ jQuery.data( this, key, value ); }); }, removeData: function( key ){ return this.each(function(){ jQuery.removeData( this, key ); }); }, domManip: function( args, table, reverse, callback ) { var clone = this.length > 1, elems; return this.each(function(){ if ( !elems ) { elems = jQuery.clean( args, this.ownerDocument ); if ( reverse ) elems.reverse(); } var obj = this; if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) ) obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") ); var scripts = jQuery( [] ); jQuery.each(elems, function(){ var elem = clone ? jQuery( this ).clone( true )[0] : this; // execute all scripts after the elements have been injected if ( jQuery.nodeName( elem, "script" ) ) scripts = scripts.add( elem ); else { // Remove any inner scripts for later evaluation if ( elem.nodeType == 1 ) scripts = scripts.add( jQuery( "script", elem ).remove() ); // Inject the elements into the document callback.call( obj, elem ); } }); scripts.each( evalScript ); }); } }; // Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn; function evalScript( i, elem ) { if ( elem.src ) jQuery.ajax({ url: elem.src, async: false, dataType: "script" }); else jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); if ( elem.parentNode ) elem.parentNode.removeChild( elem ); } function now(){ return +new Date; } jQuery.extend = jQuery.fn.extend = function() { // copy reference to target object var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; // Handle a deep copy situation if ( target.constructor == Boolean ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target != "object" && typeof target != "function" ) target = {}; // extend jQuery itself if only one argument is passed if ( length == i ) { target = this; --i; } for ( ; i < length; i++ ) // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) // Extend the base object for ( var name in options ) { var src = target[ name ], copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) continue; // Recurse if we're merging object values if ( deep && copy && typeof copy == "object" && !copy.nodeType ) target[ name ] = jQuery.extend( deep, // Never move original objects, clone them src || ( copy.length != null ? [ ] : { } ) , copy ); // Don't bring in undefined values else if ( copy !== undefined ) target[ name ] = copy; } // Return the modified object return target; }; var expando = "jQuery" + now(), uuid = 0, windowData = {}, // exclude the following css properties to add px exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, // cache defaultView defaultView = document.defaultView || {}; jQuery.extend({ noConflict: function( deep ) { window.$ = _$; if ( deep ) window.jQuery = _jQuery; return jQuery; }, // See test/unit/core.js for details concerning this function. isFunction: function( fn ) { return !!fn && typeof fn != "string" && !fn.nodeName && fn.constructor != Array && /^[\s[]?function/.test( fn + "" ); }, // check if an element is in a (or is an) XML document isXMLDoc: function( elem ) { return elem.documentElement && !elem.body || elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; }, // Evalulates a script in a global context globalEval: function( data ) { data = jQuery.trim( data ); if ( data ) { // Inspired by code by Andrea Giammarchi // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.type = "text/javascript"; if ( jQuery.browser.msie ) script.text = data; else script.appendChild( document.createTextNode( data ) ); // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709). head.insertBefore( script, head.firstChild ); head.removeChild( script ); } }, nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); }, cache: {}, data: function( elem, name, data ) { elem = elem == window ? windowData : elem; var id = elem[ expando ]; // Compute a unique ID for the element if ( !id ) id = elem[ expando ] = ++uuid; // Only generate the data cache if we're // trying to access or manipulate it if ( name && !jQuery.cache[ id ] ) jQuery.cache[ id ] = {}; // Prevent overriding the named cache with undefined values if ( data !== undefined ) jQuery.cache[ id ][ name ] = data; // Return the named cache data, or the ID for the element return name ? jQuery.cache[ id ][ name ] : id; }, removeData: function( elem, name ) { elem = elem == window ? windowData : elem; var id = elem[ expando ]; // If we want to remove a specific section of the element's data if ( name ) { if ( jQuery.cache[ id ] ) { // Remove the section of cache data delete jQuery.cache[ id ][ name ]; // If we've removed all the data, remove the element's cache name = ""; for ( name in jQuery.cache[ id ] ) break; if ( !name ) jQuery.removeData( elem ); } // Otherwise, we want to remove all of the element's data } else { // Clean up the element expando try { delete elem[ expando ]; } catch(e){ // IE has trouble directly removing the expando // but it's ok with using removeAttribute if ( elem.removeAttribute ) elem.removeAttribute( expando ); } // Completely remove the data cache delete jQuery.cache[ id ]; } }, // args is for internal usage only each: function( object, callback, args ) { var name, i = 0, length = object.length; if ( args ) { if ( length == undefined ) { for ( name in object ) if ( callback.apply( object[ name ], args ) === false ) break; } else for ( ; i < length; ) if ( callback.apply( object[ i++ ], args ) === false ) break; // A special, fast, case for the most common use of each } else { if ( length == undefined ) { for ( name in object ) if ( callback.call( object[ name ], name, object[ name ] ) === false ) break; } else for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} } return object; }, prop: function( elem, value, type, i, name ) { // Handle executable functions if ( jQuery.isFunction( value ) ) value = value.call( elem, i ); // Handle passing in a number to a CSS property return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ? value + "px" : value; }, className: { // internal only, use addClass("class") add: function( elem, classNames ) { jQuery.each((classNames || "").split(/\s+/), function(i, className){ if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) elem.className += (elem.className ? " " : "") + className; }); }, // internal only, use removeClass("class") remove: function( elem, classNames ) { if (elem.nodeType == 1) elem.className = classNames != undefined ? jQuery.grep(elem.className.split(/\s+/), function(className){ return !jQuery.className.has( classNames, className ); }).join(" ") : ""; }, // internal only, use hasClass("class") has: function( elem, className ) { return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; } }, // A method for quickly swapping in/out CSS properties to get correct calculations swap: function( elem, options, callback ) { var old = {}; // Remember the old values, and insert the new ones for ( var name in options ) { old[ name ] = elem.style[ name ]; elem.style[ name ] = options[ name ]; } callback.call( elem ); // Revert the old values for ( var name in options ) elem.style[ name ] = old[ name ]; }, css: function( elem, name, force ) { if ( name == "width" || name == "height" ) { var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; function getWH() { val = name == "width" ? elem.offsetWidth : elem.offsetHeight; var padding = 0, border = 0; jQuery.each( which, function() { padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; }); val -= Math.round(padding + border); } if ( jQuery(elem).is(":visible") ) getWH(); else jQuery.swap( elem, props, getWH ); return Math.max(0, val); } return jQuery.curCSS( elem, name, force ); }, curCSS: function( elem, name, force ) { var ret, style = elem.style; // A helper method for determining if an element's values are broken function color( elem ) { if ( !jQuery.browser.safari ) return false; // defaultView is cached var ret = defaultView.getComputedStyle( elem, null ); return !ret || ret.getPropertyValue("color") == ""; } // We need to handle opacity special in IE if ( name == "opacity" && jQuery.browser.msie ) { ret = jQuery.attr( style, "opacity" ); return ret == "" ? "1" : ret; } // Opera sometimes will give the wrong display answer, this fixes it, see #2037 if ( jQuery.browser.opera && name == "display" ) { var save = style.outline; style.outline = "0 solid black"; style.outline = save; } // Make sure we're using the right name for getting the float value if ( name.match( /float/i ) ) name = styleFloat; if ( !force && style && style[ name ] ) ret = style[ name ]; else if ( defaultView.getComputedStyle ) { // Only "float" is needed here if ( name.match( /float/i ) ) name = "float"; name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); var computedStyle = defaultView.getComputedStyle( elem, null ); if ( computedStyle && !color( elem ) ) ret = computedStyle.getPropertyValue( name ); // If the element isn't reporting its values properly in Safari // then some display: none elements are involved else { var swap = [], stack = [], a = elem, i = 0; // Locate all of the parent display: none elements for ( ; a && color(a); a = a.parentNode ) stack.unshift(a); // Go through and make them visible, but in reverse // (It would be better if we knew the exact display type that they had) for ( ; i < stack.length; i++ ) if ( color( stack[ i ] ) ) { swap[ i ] = stack[ i ].style.display; stack[ i ].style.display = "block"; } // Since we flip the display style, we have to handle that // one special, otherwise get the value ret = name == "display" && swap[ stack.length - 1 ] != null ? "none" : ( computedStyle && computedStyle.getPropertyValue( name ) ) || ""; // Finally, revert the display styles back for ( i = 0; i < swap.length; i++ ) if ( swap[ i ] != null ) stack[ i ].style.display = swap[ i ]; } // We should always get a number back from opacity if ( name == "opacity" && ret == "" ) ret = "1"; } else if ( elem.currentStyle ) { var camelCase = name.replace(/\-(\w)/g, function(all, letter){ return letter.toUpperCase(); }); ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; // From the awesome hack by Dean Edwards // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 // If we're not dealing with a regular pixel number // but a number that has a weird ending, we need to convert it to pixels if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { // Remember the original values var left = style.left, rsLeft = elem.runtimeStyle.left; // Put in the new values to get a computed value out elem.runtimeStyle.left = elem.currentStyle.left; style.left = ret || 0; ret = style.pixelLeft + "px"; // Revert the changed values style.left = left; elem.runtimeStyle.left = rsLeft; } } return ret; }, clean: function( elems, context ) { var ret = []; context = context || document; // !context.createElement fails in IE with an error but returns typeof 'object' if (typeof context.createElement == 'undefined') context = context.ownerDocument || context[0] && context[0].ownerDocument || document; jQuery.each(elems, function(i, elem){ if ( !elem ) return; if ( elem.constructor == Number ) elem += ''; // Convert html string into DOM nodes if ( typeof elem == "string" ) { // Fix "XHTML"-style tags in all browsers elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? all : front + ">"; }); // Trim whitespace, otherwise indexOf won't work as expected var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div"); var wrap = // option or optgroup !tags.indexOf("", "" ] || !tags.indexOf("", "" ] || tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && [ 1, "", "
    " ] || !tags.indexOf("", "" ] || // matched above (!tags.indexOf("", "" ] || !tags.indexOf("", "" ] || // IE can't serialize and If you want to run Fudge's own tests, then cd into the ``javascript/`` directory, start a simple webserver:: $ python fudge/testserver.py and open http://localhost:8000/tests/test_fudge.html Take note that while Fudge's *tests* require jQuery, Fudge itself does not require jQuery. Usage ===== Refer to :ref:`using-fudge` in Python to get an idea for how to use the JavaScript version. As mentioned before, the JavaScript port is not yet fully implemented. Here is a quick example: .. code-block:: javascript // if you had e.g. a session object that looked something like: yourapp = {}; yourapp.session = { set: function(key, value) { // ... } } yourapp.startup = function() { yourapp.session.set('saw_landing_page',true); }; // and if you wanted to test the startup() method above, then you could // declare a fake object for a test: var fake_session = new fudge.Fake('session').expects('set').with_args('saw_landing_page',true); // patch your production code: yourapp.session = fake_session; // and run a test: fudge.clear_calls(); yourapp.startup(); fudge.verify(); fudge-1.0.3/docs/._Makefile000644 000765 000024 00000000272 11456430705 015702 0ustar00kumarstaff000000 000000 Mac OS X  2ˆºATTR詺˜"˜"com.macromates.caret{ column = 1; line = 40; }fudge-1.0.3/docs/Makefile000644 000765 000024 00000005761 11456430705 015475 0ustar00kumarstaff000000 000000 # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # this is the best I can think of # for making it obvious to the user # that he forgot to set some environ vars FD_PROJECTS_HOST ?= FD_PROJECTS_HOST FD_PROJECTS_PATH_TARGET ?= FD_PROJECTS_PATH_TARGET .PHONY: help clean html web pickle htmlhelp latex changes linkcheck help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files (usable by e.g. sphinx-web)" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf _build/* html: mkdir -p _build/html _build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html @echo @echo "Build finished. The HTML pages are in _build/html." docslive: cd _build/; cp -R html current && tar -czf current.tar.gz current scp _build/current.tar.gz $(FD_PROJECTS_HOST):$(FD_PROJECTS_PATH_TARGET)/fudge/ && \ ssh $(FD_PROJECTS_HOST) "cd $(FD_PROJECTS_PATH_TARGET)/fudge && rm -fr current && tar -xzf current.tar.gz && rm current.tar.gz" && \ rm -fr _build/current.tar.gz && \ rm -fr _build/current @echo "Docs sent to $(FD_PROJECTS_HOST):$(FD_PROJECTS_PATH_TARGET)/fudge/" doctest: mkdir -p _build/html _build/doctrees PYTHONPATH=../:./_doctest_support $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) _build/html pickle: mkdir -p _build/pickle _build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle @echo @echo "Build finished; now you can process the pickle files or run" @echo " sphinx-web _build/pickle" @echo "to start the sphinx-web server." web: pickle htmlhelp: mkdir -p _build/htmlhelp _build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex @echo @echo "Build finished; the LaTeX files are in _build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p _build/changes _build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes @echo @echo "The overview file is in _build/changes." linkcheck: mkdir -p _build/linkcheck _build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in _build/linkcheck/output.txt." fudge-1.0.3/docs/._migrating-0.9-to-1.0.rst000644 000765 000024 00000000274 11526545477 020212 0ustar00kumarstaff000000 000000 Mac OS X  2ŠŒATTRŒIŒ˜$˜$com.macromates.caret{ column = 223; line = 14; }fudge-1.0.3/docs/migrating-0.9-to-1.0.rst000644 000765 000024 00000002412 11526545477 017771 0ustar00kumarstaff000000 000000 =============================== Migrating from Fudge 0.9 to 1.0 =============================== After :ref:`many 0.9.x versions ` and some great input from the community, Fudge has evolved to 1.0. This introduces a much *simpler* API and while it doesn't deprecate the old API you'll probably want to update your code. Take a look at the new code examples in :ref:`using Fudge ` to get a feel for it. Here is a summary of changes: The new @patch and @test decorators =================================== You no longer have to worry about when and where to call :func:`fudge.clear_calls`, :func:`fudge.verify`, and :func:`fudge.clear_expectations`! Instead, just wrap each test in the :func:`fudge.patch` decorator and declare expectations within your test. If you don't need to patch anything, use the :func:`fudge.test` decorator. Expectations that were declared in setup ======================================== If you were declaring expectations in a module-level ``setup()`` or ``unittest.setUp()`` method then you either have to continue managing the clear/verify calls manually and decorate your tests with :func:`fudge.with_fakes` or you need to move all declaration into the test function (not setup) using the :func:`fudge.patch` decorator. fudge-1.0.3/docs/._using-fudge.rst000644 000765 000024 00000000274 11536722402 017210 0ustar00kumarstaff000000 000000 Mac OS X  2ŠŒATTRèÁŒ˜$˜$com.macromates.caret{ column = 32; line = 266; }fudge-1.0.3/docs/using-fudge.rst000644 000765 000024 00000025160 11536722402 016774 0ustar00kumarstaff000000 000000 .. _using-fudge: =========== Using Fudge =========== Fudging A Web Service ===================== When testing code that uses a web service you probably want a fast set of tests that don't depend on an actual web service on the Internet. This is a good scenario in which to use mock objects. Say you have a Twitter bot that looks something like this: .. doctest:: >>> import oauthtwitter >>> def post_msg_to_twitter(msg): ... api = oauthtwitter.OAuthApi( ... '', '', ... '', '' ... ) ... api.UpdateStatus(msg) ... print "Sent: %s" % msg >>> Since the `oauthtwitter`_ module is maintained independently, your code should work as long as it calls the right methods. A Simple Test Case ================== You can use Fudge to replace the OAuthApi class with a fake and **declare an expectation** of how it should be used: .. _oauthtwitter: http://code.google.com/p/oauth-python-twitter/ .. doctest:: >>> import fudge >>> @fudge.patch('oauthtwitter.OAuthApi') ... def test(FakeOAuthApi): ... (FakeOAuthApi.expects_call() ... .with_args('', '', ... '', '') ... .returns_fake() ... .expects('UpdateStatus').with_arg_count(1)) ... ... post_msg_to_twitter("hey there fellow testing freaks!") >>> Let's break this down: 1. The :func:`patch ` decorator will temporarily patch in a fake object for the duration of the test and expose it as an argument to the test. This allows you to add expectations or stubs. 2. The :class:`fake ` object you see here expects a call (class instantiation) with four arguments having specific string values. The returned value is an object instance (a new fake) that expects you to call ``fake_oauth.UpdateStatus()`` with one argument. 3. Finally, ``post_msg_to_twitter()`` is called. Let's run the test! .. doctest:: >>> test() Sent: hey there fellow testing freaks! Sweet, it passed. Fudge lets you declare expectations as loose or as tight as you want. If you don't care about the exact arguments, you can leave off the call to :meth:`fudge.Fake.with_args`. If you don't care if a method is actually called you can use :meth:`fudge.Fake.provides` instead of :meth:`fudge.Fake.expects`. Likewise, :meth:`fudge.Fake.with_arg_count` can be used when you don't want to worry about the actual argument values. There are `argument inspectors `_ for checking values in other ways. Fake objects without patches (dependency injection) =================================================== If you don't need to patch anything, you can use the :func:`fudge.test` decorator instead. This will ensure an exception is raised in case any expectations aren't met. Here's an example: .. doctest:: >>> def send_msg(api): ... if False: # a mistake ... api.UpdateStatus('hello') ... >>> @fudge.test ... def test_msg(): ... FakeOAuthApi = (fudge.Fake('OAuthApi') ... .is_callable() ... .expects('UpdateStatus')) ... api = FakeOAuthApi() ... send_msg(api) ... >>> test_msg() Traceback (most recent call last): ... AssertionError: fake:OAuthApi.UpdateStatus() was not called .. doctest:: :hide: >>> fudge.clear_expectations() Stubs Without Expectations ========================== If you want a fake object where the methods can be called but are not expected to be called, the code is just the same but instead of :meth:`Fake.expects() ` you use :meth:`Fake.provides() `. Here is an example of always returning True for the method is_logged_in(): .. doctest:: :hide: >>> import fudge .. doctest:: >>> def show_secret_word(): ... import auth ... user = auth.current_user() ... if user.is_logged_in(): ... print "Bird is the word" ... else: ... print "Access denied" ... >>> @fudge.patch('auth.current_user') ... def test_secret_word(current_user): ... user = current_user.expects_call().returns_fake() ... user = user.provides('is_logged_in').returns(True) ... show_secret_word() ... >>> test_secret_word() Bird is the word Note that if ``user.is_logged_in()`` is not called then no error will be raised because it's provided, not expected: Replacing A Method ================== Sometimes returning a static value isn't good enough, you actually need to run some code. You can do this using :meth:`Fake.calls() ` like this: .. doctest:: >>> auth = fudge.Fake() >>> def check_user(username): ... if username=='bert': ... print "Bird is the word" ... else: ... print "Access denied" ... >>> auth = auth.provides('show_secret_word_for_user').calls(check_user) >>> # Now, the check_user function gets called instead: >>> auth.show_secret_word_for_user("bert") Bird is the word >>> auth.show_secret_word_for_user("ernie") Access denied Cascading Objects ================= Some objects you might want to work with will spawn a long chain of objects. Here is an example of fudging a cascading `SQLAlchemy query `_. Notice that :meth:`Fake.returns_fake() ` is used to specify that ``session.query(User)`` should return a new object. Notice also that because query() should be iterable, it is set to return a list of fake User objects. .. doctest:: >>> import fudge >>> session = fudge.Fake('session') >>> query = (session.provides('query') ... .returns_fake() ... .provides('order_by') ... .returns( ... [fudge.Fake('User').has_attr(name='Al', lastname='Capone')] ... ) ... ) >>> from models import User >>> for instance in session.query(User).order_by(User.id): ... print instance.name, instance.lastname ... Al Capone Multiple Return Values ====================== Let's say you want to test code that needs to call a function multiple times and get back multiple values. Up until now, you've just seen the :meth:`Fake.returns() ` method which will return a value infinitely. To change that, call :meth:`Fake.next_call() ` to advance the call sequence. Here is an example using a shopping cart scenario: .. doctest:: >>> cart = (fudge.Fake('cart') ... .provides('add') ... .with_args('book') ... .returns({'contents': ['book']}) ... .next_call() ... .with_args('dvd') ... .returns({'contents': ['book', 'dvd']})) >>> cart.add('book') {'contents': ['book']} >>> cart.add('dvd') {'contents': ['book', 'dvd']} >>> cart.add('monkey') Traceback (most recent call last): ... AssertionError: This attribute of fake:cart can only be called 2 time(s). Expecting A Specific Call Order =============================== You may need to test an object that expects its methods to be called in a specific order. Just preface any calls to :func:`fudge.Fake.expects` with :func:`fudge.Fake.remember_order` like this: .. doctest:: >>> import fudge >>> session = (fudge.Fake("session").remember_order() ... .expects("get_count").returns(0) ... .expects("set_count").with_args(5) ... .expects("get_count").returns(5)) ... >>> session.get_count() 0 >>> session.set_count(5) >>> session.get_count() 5 >>> fudge.verify() A descriptive error is printed if you call things out of order: .. doctest:: :hide: >>> fudge.clear_calls() .. doctest:: >>> session.set_count(5) Traceback (most recent call last): ... AssertionError: Call #1 was fake:session.set_count(5); Expected: #1 fake:session.get_count()[0], #2 fake:session.set_count(5), #3 fake:session.get_count()[1], end .. doctest:: :hide: >>> fudge.clear_expectations() .. _creating-a-stub: Allowing any call or attribute (a complete stub) ================================================ If you need an object that lazily provides any call or any attribute then you can declare :func:`fudge.Fake.is_a_stub`. Any requested method or attribute will always return a new :class:`fudge.Fake` instance making it easier to work complex objects. Here is an example: .. doctest:: >>> Server = fudge.Fake('xmlrpclib.Server').is_a_stub() >>> pypi = Server('http://pypi.python.org/pypi') >>> pypi.list_packages() fake:xmlrpclib.Server().list_packages() >>> pypi.package_releases() fake:xmlrpclib.Server().package_releases() Stubs like this carry on infinitely: .. doctest:: >>> f = fudge.Fake('base').is_a_stub() >>> f.one.two.three().four fake:base.one.two.three().four .. note:: When using :func:`fudge.Fake.is_a_stub` you can't lazily access any attributes or methods if they have the same name as a Fake method, like ``returns()`` or ``with_args()``. You would need to declare expectations for those directly using :func:`fudge.expects`, etc. .. _working-with-arguments: Working with Arguments ====================== The :func:`fudge.Fake.with_args` method optionally allows you to declare expectations of how arguments should be sent to your object. It's usually sufficient to expect an exact argument value but sometimes you need to use :mod:`fudge.inspector` functions for dynamic values. Here is a short example: .. doctest:: >>> import fudge >>> from fudge.inspector import arg >>> image = (fudge.Fake("image") ... .expects("save") ... .with_args("JPEG", arg.endswith(".jpg"), resolution=arg.any()) ... ) This declaration is very flexible; it allows the following calls: .. doctest:: >>> image.save("JPEG", "/tmp/unicorns-and-rainbows.jpg", resolution=72) >>> image.save("JPEG", "/tmp/me-being-serious.jpg", resolution=96) .. doctest:: :hide: >>> fudge.verify() >>> fudge.clear_expectations() .. _Nose: http://somethingaboutorange.com/mrl/projects/nose/ .. _py.test: http://codespeak.net/py/dist/test.html That's it! See the fudge API for details: .. toctree:: :glob: api/*fudge-1.0.3/docs/why-fudge.rst000644 000765 000024 00000006363 11456430706 016466 0ustar00kumarstaff000000 000000 =========================== Why Another Mock Framework? =========================== Can't you do most of this in plain old Python? If you're just replacing methods then yes but when you need to manage expectations, it's not so easy. Fudge started when a co-worker showed me `Mocha `_ for Ruby. I liked it because it was a much simpler version of `jMock`_ and jMock allows you to do two things at once: 1) build a fake version of a real object and 2) inspect that your code uses it correctly (post mortem). Up until now, I've built all my mock logic in plain Python and noticed that I spent gobs of code doing these two things in separate places. The jMock approach gets rid of the need for a post mortem and the expectation code is very readable. What about all the other mock frameworks for Python? I *really* didn't want to build another mock framework, honestly. Here were my observations of the scenery: - `pMock `_ (based on `jMock`_) - This of course is based on the same jMock interface that I like. However, its site claims it has not been maintained since 2004 and besides that jMock is too over engineered for my tastes and pMock does not attempt to fix that. - `minimock `_ - As far as I can tell, there is no easy, out-of-the-box way to use minimock in anything other than a doctest. - It doesn't really deal with expectations, just replacements (stubbing). - `mock `_ - I didn't like how mock focused on post mortem inspection. - `pyMock `_ (based on `EasyMock`_) - This uses a record / playback technique whereby you act upon your real objects then flip a switch and they become fake. This seems like it has some benefits for maintenance but I'm not sure that the overhead of recording with real objects is worth it. I suppose you'd need a real database, a real web service, etc. - `Mox `_ (based on `EasyMock`_) - This also uses the record / playback technique but with a DSL (domain specific language). It was brought to my attention after creating Fudge but thought it was worth mentioning. - `mocker `_ (based on `EasyMock`_ and others) - This was also `pointed out to me `_ after developing Fudge. - Mocker is another record / playback implementation but seems to have a cleaner interface than most. I still do not see the practicality of record / playback. How do you write tests in record mode? I am probably missing it but nowhere in the docs do I see practical examples for creating test code. Instead the examples are interactive sessions which is not how I typically write tests. - The docs for mocker, like docs for other tools, do not focus on any real-world problem that a mock framework can solve. This is hard for my brain. It is hard for me to look at code such as obj.hello() and imagine that this would be useful for, say, mocking out sendmail(). - However, mocker certainly looks like it has more features than Fudge so it is worth checking out. .. _jMock: http://www.jmock.org/ .. _EasyMock: http://www.easymock.org/fudge-1.0.3/docs/api/fudge.inspector.rst000644 000765 000024 00000000222 11456430705 020422 0ustar00kumarstaff000000 000000 ---------------- fudge.inspector ---------------- .. automodule:: fudge.inspector .. autoclass:: fudge.inspector.ValueInspector :members:fudge-1.0.3/docs/api/fudge.patcher.rst000644 000765 000024 00000000454 11456430705 020051 0ustar00kumarstaff000000 000000 .. _fudge.patcher: ------------- fudge.patcher ------------- .. automodule:: fudge.patcher .. autofunction:: fudge.patcher.with_patched_object .. autofunction:: fudge.patcher.patched_context .. autofunction:: fudge.patcher.patch_object .. autoclass:: fudge.patcher.PatchHandler :members:fudge-1.0.3/docs/api/._fudge.rst000644 000765 000024 00000000272 11526541714 016640 0ustar00kumarstaff000000 000000 Mac OS X  2ˆºATTR蜺˜"˜"com.macromates.caret{ column = 28; line = 9; }fudge-1.0.3/docs/api/fudge.rst000644 000765 000024 00000000462 11526541714 016424 0ustar00kumarstaff000000 000000 ----- fudge ----- .. automodule:: fudge .. autofunction:: fudge.patch .. autofunction:: fudge.test .. autoclass:: fudge.Fake :members: .. autofunction:: fudge.clear_calls .. autofunction:: fudge.verify .. autofunction:: fudge.with_fakes .. autoclass:: fudge.FakeDeclarationError :members:fudge-1.0.3/docs/_static/.hidden000644 000765 000024 00000000000 11456430705 016674 0ustar00kumarstaff000000 000000 fudge-1.0.3/docs/_doctest_support/auth/000755 000765 000024 00000000000 11536723452 020370 5ustar00kumarstaff000000 000000 fudge-1.0.3/docs/_doctest_support/models.py000644 000765 000024 00000000124 11456430705 021256 0ustar00kumarstaff000000 000000 # this is so the SQLAlchemy doctest will work. class User(object): id = 1fudge-1.0.3/docs/_doctest_support/oauthtwitter/000755 000765 000024 00000000000 11536723452 022172 5ustar00kumarstaff000000 000000 fudge-1.0.3/docs/_doctest_support/oauthtwitter/__init__.py000644 000765 000024 00000000255 11456430705 024302 0ustar00kumarstaff000000 000000 # this exists so the doctest can import oauthtwitter before fudging out its methods def OAuthApi(*args, **kw): raise RuntimeError("Test failed to mock out this method")fudge-1.0.3/docs/_doctest_support/auth/.___init__.py000644 000765 000024 00000000272 11526423453 022714 0ustar00kumarstaff000000 000000 Mac OS X  2ˆºATTR謺˜"˜"com.macromates.caret{ column = 16; line = 3; }fudge-1.0.3/docs/_doctest_support/auth/__init__.py000644 000765 000024 00000000236 11526423453 022477 0ustar00kumarstaff000000 000000 # these get replaced by fakes in the doctest def current_user(*args): raise NotImplementedError def login(*args): raise NotImplementedError