mirror of
https://github.com/mat007/turtle.git
synced 2026-06-22 12:13:43 +00:00
616 lines
23 KiB
Text
616 lines
23 KiB
Text
[/
|
|
/ Copyright (c) 2014 Mathieu Champlon
|
|
/
|
|
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
/]
|
|
|
|
[section Reference]
|
|
[import example/reference.cpp]
|
|
|
|
This section describes the library syntax exhaustively.
|
|
|
|
All source code snippets assume the following prerequisite :
|
|
|
|
[prerequisite]
|
|
|
|
[section Creation]
|
|
|
|
Mock objects can be assigned and copied around freely, unless they derive from a type which disables it.
|
|
|
|
Copies of a mock object share the same internal state, meaning setting an expectation on one of them will impact all of them. Thus it is possible to let an object under test copy a mock object and still be able to set, verify or reset expectations.
|
|
|
|
The library defines a set of convenient macros for creating mock objects of different kinds :
|
|
|
|
* classes
|
|
* functors
|
|
* functions
|
|
|
|
Creating a mock object involves two parts under the hood :
|
|
|
|
* defining an object
|
|
* declaring an identifier for manipulating the object
|
|
|
|
Most of the time the identifier will be identical to the object name, but in case of ambiguity (for instance overloaded methods) a different identifier will have to be specified.
|
|
|
|
[warning Creating a mock object creates a new object and does not magically replace existing ones, for instance creating a mock function will not replace an already existing function with the same name and signature.]
|
|
|
|
[section Class]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_CLASS( name ) // defines a class
|
|
MOCK_BASE_CLASS( name, base ) // defines a class deriving from a base class
|
|
|
|
The preferred form for defining a mock class is with using MOCK_CLASS and MOCK_BASE_CLASS, however using a regular struct or class is also perfectly fine.
|
|
|
|
Example :
|
|
|
|
[class_example_1]
|
|
|
|
Example :
|
|
|
|
[class_example_2]
|
|
|
|
Example :
|
|
|
|
[class_example_3]
|
|
|
|
Example :
|
|
|
|
[class_example_4]
|
|
|
|
Example :
|
|
|
|
[class_example_5]
|
|
|
|
Example :
|
|
|
|
[class_example_6]
|
|
|
|
Example :
|
|
|
|
[class_example_7]
|
|
|
|
Example :
|
|
|
|
[class_example_8]
|
|
|
|
Deriving from mock::object is optional but provides the additional following benefits :
|
|
|
|
* the object acts as a composite to [link turtle.reference.verification verify] and [link turtle.reference.reset reset] all the expectations for all its methods at once
|
|
* logs involving the object are enhanced because configuring an expectation for a method will set the class name for all the other methods as well
|
|
|
|
[endsect]
|
|
|
|
[section Member function]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_METHOD( [calling convention] name, arity[, signature[, identifier]] ) // generates both const and non-const methods
|
|
MOCK_CONST_METHOD( [calling convention] name, arity[, signature[, identifier]] ) // generates only the const version of the method
|
|
MOCK_NON_CONST_METHOD( [calling convention] name, arity[, signature[, identifier]] ) // generates only the non-const version of the method
|
|
|
|
MOCK_METHOD_TPL( [calling convention] name, arity, signature[, identifier] ) // must be used if the signature uses a template parameter of the class, generates both const and non-const methods
|
|
MOCK_CONST_METHOD_TPL( [calling convention] name, arity, signature[, identifier] ) // must be used if the signature uses a template parameter of the class, generates only the const version of the method
|
|
MOCK_NON_CONST_METHOD_TPL( [calling convention] name, arity, signature[, identifier] ) // must be used if the signature uses a template parameter of the class, generates only the non-const version of the method
|
|
|
|
[note If the identifier is omitted it will default to the method name.]
|
|
|
|
[note If the method name is not ambiguous both the signature and the identifier can be omitted in the context of a derived MOCK_BASE_CLASS or base_type typedef.]
|
|
|
|
[note The signature cannot be omitted for the _TPL familly of macros, see the related [link turtle.limitations.template_base_class_methods_cannot_be_mocked_without_specifying_the_signature limitation section].]
|
|
|
|
[note The signature must be surrounded with round parenthesis if the return type contains a comma.]
|
|
|
|
[note [link turtle.reference.creation.constructor Constructors], [link turtle.reference.creation.destructor destructors] and [link turtle.reference.creation.conversion_operator conversion operators] require special care.]
|
|
|
|
[note In case of a calling convention specified, all four parameters must be provided.]
|
|
|
|
[warning For compilers without support for variadic macros the MOCK_METHOD_EXT familly set of macros must be used.]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_METHOD_EXT( [calling convention] name, arity, signature, identifier ) // generates both const and non-const methods
|
|
MOCK_CONST_METHOD_EXT( [calling convention] name, arity, signature, identifier ) // generates only the const version of the method
|
|
MOCK_NON_CONST_METHOD_EXT( [calling convention] name, arity, signature, identifier ) // generates only the non-const version of the method
|
|
|
|
MOCK_METHOD_EXT_TPL( [calling convention] name, arity, signature, identifier ) // must be used if the signature uses a template parameter of the class, generates both const and non-const methods
|
|
MOCK_CONST_METHOD_EXT_TPL( [calling convention] name, arity, signature, identifier ) // must be used if the signature uses a template parameter of the class, generates only the const version of the method
|
|
MOCK_NON_CONST_METHOD_EXT_TPL( [calling convention] name, arity, signature, identifier ) // must be used if the signature uses a template parameter of the class, generates only the non-const version of the method
|
|
|
|
Example :
|
|
|
|
[member_function_example_1]
|
|
|
|
Example :
|
|
|
|
[member_function_example_2]
|
|
|
|
Example :
|
|
|
|
[member_function_example_3]
|
|
|
|
Example :
|
|
|
|
[member_function_example_4]
|
|
|
|
Example :
|
|
|
|
[member_function_example_6]
|
|
|
|
Example :
|
|
|
|
[member_function_example_7]
|
|
|
|
Example :
|
|
|
|
[member_function_example_8]
|
|
|
|
Example for msvc :
|
|
|
|
[member_function_example_9]
|
|
|
|
Example for gcc :
|
|
|
|
[member_function_example_10]
|
|
|
|
[endsect]
|
|
|
|
[section Static member function]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_STATIC_METHOD( [calling convention] name, arity, signature[, identifier] ) // if 'identifier' is omitted it will default to 'name'
|
|
|
|
MOCK_STATIC_METHOD_TPL( [calling convention] name, arity, signature[, identifier] ) // must be used if the signature uses a template parameter of the class, if 'identifier' is omitted it will default to 'name'
|
|
|
|
[note A static object is used behind the scene in order to keep track of the expectations of a mock static method, therefore to ensure all tests run in isolation it is strongly suggested to manually [link turtle.reference.verification verify] and [link turtle.reference.reset reset] the static method at the end of each test.]
|
|
|
|
[note In case of a calling convention specified, all four parameters must be provided.]
|
|
|
|
[warning For compilers without support for variadic macros the identifier cannot be omitted and must be given explicitly.]
|
|
|
|
Example :
|
|
|
|
[static_member_function_example_1]
|
|
|
|
Example :
|
|
|
|
[static_member_function_example_2]
|
|
|
|
Example for msvc :
|
|
|
|
[static_member_function_example_3]
|
|
|
|
Example for gcc :
|
|
|
|
[static_member_function_example_4]
|
|
|
|
[endsect]
|
|
|
|
[section Constructor]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_CONSTRUCTOR( [calling convention] name, arity, parameters, identifier )
|
|
|
|
MOCK_CONSTRUCTOR_TPL( [calling convention] name, arity, parameters, identifier ) // must be used if the signature uses a template parameter of the class
|
|
|
|
[note As constructors do not have a return type, the usual signature gets restricted here to just the parameters.]
|
|
|
|
Example :
|
|
|
|
[constructor_example_1]
|
|
|
|
Example :
|
|
|
|
[constructor_example_2]
|
|
|
|
Example for msvc :
|
|
|
|
[constructor_example_3]
|
|
|
|
Example for gcc :
|
|
|
|
[constructor_example_4]
|
|
|
|
[endsect]
|
|
|
|
[section Destructor]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_DESTRUCTOR( [calling convention] ~name, identifier )
|
|
|
|
[note When mocking a destructor it is strongly suggested to manually [link turtle.reference.verification verify] the expectation at the end of the test, because the automatic verification will not be triggered if the mock object is not destroyed.]
|
|
|
|
Example :
|
|
|
|
[destructor_example_1]
|
|
|
|
Example for msvc :
|
|
|
|
[destructor_example_2]
|
|
|
|
Example for gcc :
|
|
|
|
[destructor_example_3]
|
|
|
|
[endsect]
|
|
|
|
[section Conversion operator]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_CONVERSION_OPERATOR( [calling convention] name, type, identifier ) // generates both const and non-const operators
|
|
MOCK_CONST_CONVERSION_OPERATOR( [calling convention] name, type, identifier ) // generates only a const operator
|
|
MOCK_NON_CONST_CONVERSION_OPERATOR( [calling convention] name, type, identifier ) // generates only a non-const operator
|
|
|
|
MOCK_CONVERSION_OPERATOR_TPL( [calling convention] name, type, identifier ) // must be used if the signature uses a template parameter of the class
|
|
MOCK_CONST_CONVERSION_OPERATOR_TPL( [calling convention] name, type, identifier ) // must be used if the signature uses a template parameter of the class
|
|
MOCK_NON_CONST_CONVERSION_OPERATOR_TPL( [calling convention] name, type, identifier ) // must be used if the signature uses a template parameter of the class
|
|
|
|
Example :
|
|
|
|
[conversion_operator_example_1]
|
|
|
|
Example :
|
|
|
|
[conversion_operator_example_2]
|
|
|
|
Example for msvc :
|
|
|
|
[conversion_operator_example_3]
|
|
|
|
Example for gcc :
|
|
|
|
[conversion_operator_example_4]
|
|
|
|
[endsect]
|
|
|
|
[section Function]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_FUNCTION( [calling convention] name, arity, signature[, identifier] ) // if 'identifier' is omitted it will default to 'name'
|
|
|
|
[note A static object is used behind the scene in order to keep track of the expectations of a mock function, therefore to ensure all tests run in isolation it is strongly suggested to manually [link turtle.reference.verification verify] and [link turtle.reference.reset reset] the mock function at the end of each test.]
|
|
|
|
[note In case of a calling convention specified, all four parameters must be provided.]
|
|
|
|
[warning For compilers without support for variadic macros the identifier cannot be omitted and must be given explicitly.]
|
|
|
|
Example :
|
|
|
|
[function_example_1]
|
|
|
|
Example for msvc :
|
|
|
|
[function_example_2]
|
|
|
|
Example for gcc :
|
|
|
|
[function_example_3]
|
|
|
|
[endsect]
|
|
|
|
[section Functor]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_FUNCTOR( [calling convention] name, signature );
|
|
|
|
MOCK_FUNCTOR_TPL( [calling convention] name, signature ); // must be used if the signature uses a template parameter
|
|
|
|
Example :
|
|
|
|
[functor_example_1]
|
|
|
|
Example :
|
|
|
|
[functor_example_2]
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|
|
|
|
[section Expectation]
|
|
|
|
An expectation is a statement of configuration for a mock object.
|
|
|
|
Synopsis :
|
|
|
|
MOCK_EXPECT( identifier ).``[link turtle.reference.expectation.invocation invocation]``( arguments ).with( ``[link turtle.reference.expectation.constraints constraints]`` ).in( ``[link turtle.reference.expectation.sequence sequences]`` ).``[link turtle.reference.expectation.actions action]``( value );
|
|
|
|
[note The identifier refers to the one specified when [link turtle.reference.creation creating] a mock object.]
|
|
|
|
Example :
|
|
|
|
[expectation_example_1]
|
|
|
|
[section Invocation]
|
|
|
|
An invocation defines how many times a mock object is to be exercised.
|
|
|
|
Synopsis :
|
|
|
|
MOCK_EXPECT( identifier ); // any number of times including never
|
|
MOCK_EXPECT( identifier ).once();
|
|
MOCK_EXPECT( identifier ).never();
|
|
MOCK_EXPECT( identifier ).exactly( count );
|
|
MOCK_EXPECT( identifier ).at_least( min );
|
|
MOCK_EXPECT( identifier ).at_most( max );
|
|
MOCK_EXPECT( identifier ).between( min, max ); // throws std::invalid_argument if 'min' > 'max'
|
|
|
|
Example :
|
|
|
|
[invocation_example_1]
|
|
|
|
Example :
|
|
|
|
[invocation_example_2]
|
|
|
|
Example :
|
|
|
|
[invocation_example_3]
|
|
|
|
Example :
|
|
|
|
[invocation_example_4]
|
|
|
|
[endsect]
|
|
|
|
[section Constraints]
|
|
|
|
Constraints validate the actual parameter values of a call to a mock object.
|
|
|
|
Synopsis :
|
|
|
|
MOCK_EXPECT( identifier ).with( constraint ); // one constraint for all parameters
|
|
MOCK_EXPECT( identifier ).with( constraint_1, constraint_2, ... ); // one constraint for each parameter
|
|
|
|
In the following table 'expected' denotes a user supplied data whereas 'actual' stands for the one or several parameter values received in a call to a mock function.
|
|
|
|
Constraints :
|
|
|
|
[table
|
|
[[Constraint] [Effect] [Description]]
|
|
[[mock::any] [true] [does not perform any verification]]
|
|
[[['expected]] [['expected]( ['actual] )
|
|
|
|
['expected]( ['actual_1], ['actual_2], ... )
|
|
|
|
['actual] == ['expected]] [calls ['expected] as a functor returning a ['bool], throws std::invalid_argument if ! ['expected]
|
|
|
|
calls ['expected] as a functor returning a ['bool], throws std::invalid_argument if ! ['expected]
|
|
|
|
compares ['actual] to ['expected] using operator ==]]
|
|
[[mock::equal( ['expected] )] [['actual] == ['expected]] [compares ['actual] to ['expected] using operator ==]]
|
|
[[mock::less( ['expected] )] [['actual] < ['expected]] [compares ['actual] to ['expected] using operator <]]
|
|
[[mock::greater( ['expected] )] [['actual] > ['expected]] [compares ['actual] to ['expected] using operator >]]
|
|
[[mock::less_equal( ['expected] )] [['actual] <= ['expected]] [compares ['actual] to ['expected] using operator <=]]
|
|
[[mock::greater_equal( ['expected] )] [['actual] >= ['expected]] [compares ['actual] to ['expected] using operator >=]]
|
|
[[mock::near( ['expected], ['tolerance] )] [std::abs( ['actual] - ['expected] ) < ['tolerance]] [checks whether ['actual] is near ['expected] within ['tolerance]]]
|
|
[[mock::close( ['expected], ['tolerance] )] [boost::test_tools::check_is_close( ['actual], ['expected], boost::test_tools::percent_tolerance( ['arg] ) )] [checks whether ['actual] is close to ['expected], see [@http://www.boost.org/libs/test/doc/html/utf/testing-tools/floating_point_comparison.html Floating-point comparison algorithms] ]]
|
|
[[mock::close_fraction( ['expected], ['tolerance] )] [boost::test_tools::check_is_close( ['actual], ['expected], boost::test_tools::fraction_tolerance( ['arg] ) )] [checks whether ['actual] is close to ['expected], see [@http://www.boost.org/libs/test/doc/html/utf/testing-tools/floating_point_comparison.html Floating-point comparison algorithms] ]]
|
|
[[mock::small( ['tolerance] )] [boost::test_tools::check_is_small( ['actual], ['expected] ) )] [checks whether ['actual] is small within ['tolerance], see [@http://www.boost.org/libs/test/doc/html/utf/testing-tools/floating_point_comparison.html Floating-point comparison algorithms] ]]
|
|
[[mock::call( ['expected] )] [['expected]( ['actual] )] [calls ['expected] as a functor returning a ['bool] and accepting ['actual] as parameter]]
|
|
[[mock::same( ['expected] )] [&['actual] == &['expected]] [compares ['actual] to ['expected] by comparing their pointers]]
|
|
[[mock::assign( ['expected] )] [['actual] = ['expected], true
|
|
|
|
*['actual] = ['expected], true] [assigns ['expected] to ['actual] using operator =
|
|
|
|
assigns ['expected] to ['actual] content using operator =]]
|
|
[[mock::retrieve( ['expected] )] [['expected] = ['actual], true
|
|
|
|
['expected] = &['actual], true] [retrieves ['actual] into ['expected] using operator =
|
|
|
|
retrieves ['actual] address into ['expected] using operator =]]
|
|
[[mock::contain( ['expected] )] [['actual].find( ['expected] ) != std::string::npos] [checks whether ['expected] is contained in the std::string ['actual]]]
|
|
[[mock::affirm] [['actual]] [uses ['actual] as a boolean]]
|
|
[[mock::negate] [! ['actual]] [negates ['actual] using operator !]]
|
|
[[mock::evaluate] [['actual]()] [evaluates ['actual] as a functor returning a bool and taking no argument]]
|
|
]
|
|
|
|
[important When passing ['expected] directly as a shortcut mock::call is implied for a function, a function pointer, an instance of a type with a result_type member typedef (support for standard library, [@http://www.boost.org/libs/bind/bind.html Boost.Bind], [@http://www.boost.org/libs/function Boost.Function] functors), an instance of a type with a sig member (support for [@http://www.boost.org/libs/lambda Boost.Lambda] functors), an instance of a type with a result member (support for [@http://www.boost.org/libs/phoenix Boost.Phoenix] functors); mock::equal is implied for anything else.]
|
|
|
|
[warning Because mock::assign and mock::retrieve have side effects they may modify ['expected] in unexpected ways. For instance they may be called again after their expectations have already been exhausted because of the way the [link turtle.getting_started.expectation_selection_algorithm expectation selection algorithm] works. Therefore it is probably a good idea to use an [link turtle.reference.expectation.actions action] instead.]
|
|
|
|
[note For mock::assign and mock::retrieve the switch to one form or another is made depending on whichever is the most relevant based on types involved.]
|
|
|
|
[note All constraints accepting a parameter support the use of boost::ref and boost::cref in order to delay initialization.]
|
|
|
|
[note All constraints can be combined using the && and || operators, as well as negated with the ! operator.]
|
|
|
|
Example :
|
|
|
|
[constraints_example_1]
|
|
|
|
Example using a function pointer :
|
|
|
|
[constraints_example_2]
|
|
|
|
Example using a standard library functor :
|
|
|
|
[constraints_example_3]
|
|
|
|
Example using [@http://www.boost.org/libs/bind Boost.Bind] :
|
|
|
|
[constraints_example_4]
|
|
|
|
Example using [@http://www.boost.org/libs/lambda Boost.Lambda] :
|
|
|
|
[constraints_example_5]
|
|
|
|
Example using [@http://www.boost.org/libs/phoenix Boost.Phoenix] :
|
|
|
|
[constraints_example_6]
|
|
|
|
Example using C++11 lambdas :
|
|
|
|
[constraints_example_7]
|
|
|
|
Example using &&, || and ! :
|
|
|
|
[constraints_example_8]
|
|
|
|
Example using one constraint for all parameters :
|
|
|
|
[constraints_example_9]
|
|
|
|
[endsect]
|
|
|
|
[section Sequence]
|
|
|
|
A sequence enforces a given order between two or more expectations.
|
|
|
|
Synopsis :
|
|
|
|
MOCK_EXPECT( identifier_1 ).in( sequence_1, sequence_2, ... );
|
|
|
|
Each sequence is an instance of mock::sequence.
|
|
|
|
The maximum number of sequences that can be set is MOCK_MAX_SEQUENCES which defaults to 10. If needed the value can be increased before including the library :
|
|
|
|
#define MOCK_MAX_SEQUENCES 12
|
|
#include <turtle/mock.hpp>
|
|
|
|
Example :
|
|
|
|
[sequence_example_1]
|
|
|
|
[endsect]
|
|
|
|
[section Actions]
|
|
|
|
An action performs additional treatments after an expectation has been deemed valid.
|
|
|
|
Synopsis :
|
|
|
|
MOCK_EXPECT( identifier ).returns( value ); // stored internally by copy
|
|
MOCK_EXPECT( identifier ).moves( value ); // stored internally by copy
|
|
MOCK_EXPECT( identifier ).throws( exception ); // stored internally by copy
|
|
MOCK_EXPECT( identifier ).calls( functor ); // stored internally by copy, throws std::invalid_argument if empty
|
|
|
|
[note The returns and moves actions are not available for mock methods returning void, including constructors and destructors.]
|
|
|
|
[note Actions are captured by copy, boost::ref and boost::cref can however be used to turn the copies into references.]
|
|
|
|
Example :
|
|
|
|
[action_example_1]
|
|
|
|
Example with references :
|
|
|
|
[action_example_2]
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|
|
|
|
[section Verification]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_VERIFY( identifier );
|
|
mock::verify( object ); // verifies all expectations for all methods of 'object' which must be an instance of a class created using MOCK_CLASS or MOCK_BASE_CLASS, or inherit mock::object
|
|
mock::verify( functor ); // verifies all expectations for 'functor' which must be an instance of a functor created using MOCK_FUNCTOR
|
|
mock::verify(); // verifies all expectations for all mock objects, functions and functors
|
|
|
|
[note These calls all return a boolean indicating whether the verification was successful or not, however usually simply calling them is enough because a failing verification will be logged to the test framework.]
|
|
|
|
[note Each mock object verifies itself automatically upon destruction, which is usually sufficient for the most common use cases.]
|
|
|
|
[warning Using [link turtle.reference.creation.function mock functions] or [link turtle.reference.creation.static_member_function mock static member functions] means the associated underlying objects will not be destroyed before exiting the test application, thus it is strongly suggested to verify (and possibly [link turtle.reference.reset reset]) them at the end of each test case (for instance using a fixture) to ensure that each test runs in isolation.]
|
|
|
|
Example :
|
|
|
|
[verification_example_1]
|
|
|
|
Example :
|
|
|
|
[verification_example_2]
|
|
|
|
Example :
|
|
|
|
[verification_example_3]
|
|
|
|
Example :
|
|
|
|
[verification_example_4]
|
|
|
|
[endsect]
|
|
|
|
[section Reset]
|
|
|
|
Synopsis :
|
|
|
|
MOCK_RESET( identifier );
|
|
mock::reset( object ); // resets all expectations for all methods of 'object' which must be an instance of a class created using MOCK_CLASS or MOCK_BASE_CLASS, or inherit mock::object
|
|
mock::reset( functor ); // resets all expectations for 'functor' which must be an instance of a functor created using MOCK_FUNCTOR
|
|
mock::reset(); // resets all expectations for all mock objects, functions and functors
|
|
|
|
Example :
|
|
|
|
[reset_example_1]
|
|
|
|
Example :
|
|
|
|
[reset_example_2]
|
|
|
|
Example :
|
|
|
|
[reset_example_3]
|
|
|
|
Example :
|
|
|
|
[reset_example_4]
|
|
|
|
[endsect]
|
|
|
|
[section Constraint]
|
|
|
|
This section presents a simple means of creating a new constraint.
|
|
|
|
Synopsis :
|
|
|
|
MOCK_CONSTRAINT( name, expected_1, expected_2, ..., expression ) // defines a constraint 'name' based on the given 'expression'
|
|
|
|
The expression manipulates a received parameter ['actual] in order to implement the constraint, as well as extra optional arguments named ['expected_1], ['expected_2], ...
|
|
|
|
For compilers without support for variadic macros the alternate following macro must be used.
|
|
|
|
Synopsis :
|
|
|
|
MOCK_CONSTRAINT_EXT( name, arity, ( expected_1, expected_2, ... ), expression ) // defines a constraint 'name' based on the given 'expression'
|
|
|
|
Of course this macro is also available for compilers which support variadic macros.
|
|
|
|
Example without any extra argument :
|
|
|
|
[helpers_example_1]
|
|
|
|
or with the alternate more portable macro :
|
|
|
|
[helpers_example_4]
|
|
|
|
Example with one extra argument :
|
|
|
|
[helpers_example_2]
|
|
|
|
or with the alternate more portable macro :
|
|
|
|
[helpers_example_5]
|
|
|
|
Example with two extra arguments :
|
|
|
|
[helpers_example_3]
|
|
|
|
or with the alternate more portable macro :
|
|
|
|
[helpers_example_6]
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|