Issott_Common Design Patterns for Symbian OS-The Foundations of Smartphone Software_0470516356 (779879), страница 8
Текст из файла (страница 8)
It is when an assert fails that the currentthread is panicked.We classify asserts into two different types:• External asserts check for the design constraints imposed on how software outside the component interacts with it. An example would beclients of an API provided by the component. If an external assert failsthen it indicates that the client has used the component incorrectly.You should be able to test that these asserts fail during testing as theyeffectively form part of the API itself.• Internal asserts check for the design constraints imposed on the component itself. If an internal assert fails then it indicates that there is afault in the component that needs to be fixed. The unit tests for thecomponent should seek to test that these asserts cannot be caused tofail.FAIL FAST21Figure 2.1 illustrates how these two types of asserts are used to validatedifferent aspects of the design.ComponentProvidedInterfaceExternal validationRequiredInterfaceInternal validationchecksInternal StateFigure 2.1Structure of the Fail Fast patternSome concrete examples of where you might add asserts are:• within the implementation of a public interface• when a transition is made in a state machine so that only valid statechanges are performed• checking a class, or even a component, invariant within functions (aninvariant is a statement that can be made about the class or componentthat should remain true irrespective of what operations you performon it).Of course, there are a number of situations in which you would notwish to assert but which you would instead handle in a more sophisticatedmanner.
One such case is that of expected unexpected errors. In plainEnglish this is the set of errors that should have been considered (bydesign), but whose arrival can occur unexpectedly at any time. Often thistype of error is either a system or a domain error. A good example ofthis is the disconnection of a Bluetooth link, since it can be disconnectedat any time by a request from a remote device, noise on ‘the air’, or bymoving the devices out of radio range.Another case that typically should not be externally asserted is incorrectrequests from a client that are sensitive to some state of which the clientis not aware. For instance, a client calling Read() on a communicationsocket before Connect() has been called is a state-sensitive requestthat can be asserted since the client should be aware of the socket’s22ERROR-HANDLING STRATEGIESstate.4 However, you should not assert based on state from one clientwhen handling the request from another.
This sounds obvious but is oftenmuch less so in practice, especially if you have a state machine that canbe manipulated by multiple clients who know nothing of each other.DynamicsThe sequence chart in Figure 2.2 illustrates how a class function couldimplement a rigorous scheme of asserts. The function initially checks thatthe parameters passed in are suitable in a pre-condition checking stepas well as verifying that the object is in an appropriate state to handlethe function call.
The function then performs its main operation beforeexecuting a post-condition assert that ensures a suitable output for thefunction has been computed, before again checking that the object hasnot violated any of its invariants.CallerCalleeFunction()[optional]:PreConditionAsserts()[optional]:Invariant()MainOperation()[optional]:PostConditionAsserts()[optional]:Invariant()Figure 2.2Dynamics of the Fail Fast patternIt’s not essential that all functions conform to this sequence; thediagram simply provides a general example of how a function couldimplement this pattern.4 You should consider carefully before adding external state-sensitive asserts to aninterface because they place additional burdens on clients that use it.FAIL FAST23By using this pattern to check the design contracts internal to acomponent we have effectively produced a form of unit-test code builtinto the component.
However, these test steps are in a form that needs tobe driven as part of the standard testing for the component. This patternand testing are complementary since testing is used to show that theimplementation of a component’s design is behaving correctly (shown bythe asserts) whilst the asserts aid the debugging of any design violations(shown by the testing).ImplementationReducing the Impact of the AssertsA naı̈ve interpretation of this pattern would simply be to always checkas many conditions as are necessary to validate the implementation ofthe design. Whilst for some types of software this may be appropriate, itis often crucial to take into account the various issues that this approachwould introduce.The two main ways that asserts impact your software is through theadditional code they add to an executable and the additional executiontime needed to evaluate them.
A single assert might only add a handfulof bytes of code and take nanoseconds to evaluate. However, acrossa component, or a system, there is potentially a significant number ofasserts, especially if every function implements pre- and post-conditionchecking. For Symbian OS development, it is often simply too expensiveto use asserts liberally in production software and so we need to beselective in where they are added.One approach to this is to always enforce external asserts because wehave no control over client code and what they will try to do. However,the same is normally not true for internal asserts as these should neverbe triggered if the software is operating correctly and hence a trade-offcan be made between the performance cost of asserts and the benefitsthey bring. The simplest solution is to ensure that internal asserts areonly present in debug code.
The assumption is that you are continuallyrunning your test suite on both the debug and release versions of yourcomponent and hence can have a reasonable level of confidence that theany new faults introduced into the component will be identified duringtesting. However, the consequences of this solution are that there willalways be some release-only faults that will be let through by this methodand will not be stopped by an assert.A more sophisticated approach to dealing with the internal asserts isto change which builds they are compiled into during the developmentprocess.
Initially you would choose to have them compiled into bothrelease and debug builds until you have confidence5 that enough faults5 Thismay come from some quantifiable metric such as code coverage.24ERROR-HANDLING STRATEGIEShave been removed from your component, at which point you couldleave the asserts only in the debug builds.Implementing a Single AssertSymbian OS provides the following standard assert macros in e32def.h:• __ASSERT_ALWAYS(c,p)Here c is a conditional expression which results in true or false andp is a statement which is executed if the conditional expression c isfalse.• __ASSERT_DEBUG(c,p)The same as __ASSERT_ALWAYS(c,p) except that it is compiledout in release builds.• ASSERT(c)The same as __ASSERT_DEBUG(c,p) except that it causes a USER 0panic if c is false. Using this standard Symbian OS macro is no longerrecommended since there’s no way of identifying which ASSERT() ina component caused a USER 0.
Hence, it is common for componentsto redefine the macro to provide more details that allow the asserts tobe distinguished during debugging.• __ASSERT_COMPILE(c)This macro asserts that c is true at compilation time and is particularly useful for checking hard-coded constants although the errormessages it causes the compiler to output could be more informative.In addition, you can use the following macros to help check class invariants in a consistent manner:• __DECLARE_TESTThis should be added as the last item in a class declaration sothat a function called __DbgTestInvariant() is declared. Itis your responsibility to implement the invariant checks and callUser::Invariant() to panic the thread if they fail.• __TEST_INVARIANTThis calls the __DbgTestInvariant() function in debug builds.For more information on these macros, please see the Symbian Developer Library.However, you still need to know how to cause a panic if the assertfails.