Friday, December 4, 2009

Chapter 1. What Is Software Quality?




I l@ve RuBoard










Chapter 1. What Is Software Quality?


Quality must be defined and measured if improvement is to be achieved. Yet, a major problem in quality engineering and management is that the term quality is ambiguous, such that it is commonly misunderstood. The confusion may be attributed to several reasons. First, quality is not a single idea, but rather a multidimensional concept. The dimensions of quality include the entity of interest, the viewpoint on that entity, and the quality attributes of that entity. Second, for any concept there are levels of abstraction; when people talk about quality, one party could be referring to it in its broadest sense, whereas another might be referring to its specific meaning. Third, the term quality is a part of our daily language and the popular and professional uses of it may be very different.


In this chapter we discuss the popular views of quality, its formal definitions by quality experts and their implications, the meaning and specific uses of quality in software, and the approach and key elements of total quality management.








    I l@ve RuBoard



    15.9 Polymorphism and Dynamic Binding













    15.9 Polymorphism and Dynamic Binding


    Limited use of polymorphism and dynamic binding is easily addressed by unfolding polymorphic calls, considering each method that can be dynamically bound to each polymorphic call. Complete unfolding is impractical when many references may each be bound to instances of several subclasses.


    Consider, for example, the code fragment in Figure 15.15. Object Account may by an instance of any of the classes USAccount, UKAccount, EUAccount, JPAccount, or OtherAccount. Method validateCredit can be dynamically bound to methods validate- Credit of any of the classes EduCredit, BizCredit, or IndividualCredit, each implementing different credit policies. Parameter creditCard may be dynamically bound to VISACard, AmExpCard, or ChipmunkCard, each with different characteristics. Even in this simple example, replacing the calls with all possible instances results in 45 different cases (5 possible types of account × 3 possible types of credit × 3 possible credit cards).












    1 abstract class Credit {
    15 ...
    16 abstract boolean validateCredit( Account a, int amt, CreditCard c);
    60 ...
    61 }










    Figure 15.15: A method call in which the method itself and two of its parameters can be dynamically bound to different classes.

    The explosion in possible combinations is essentially the same combinatorial explosion encountered if we try to cover all combinations of attributes in functional testing, and the same solutions are applicable. The combinatorial testing approach presented in Chapter 11 can be used to choose a set of combinations that covers each pair of possible bindings (e.g., Business account in Japan, Education customer using Chipmunk Card), rather than all possible combinations (Japanese business customer using Chipmunk card). Table 15.4 shows 15 cases that cover all pairwise combinations of calls for the example of Figure 15.15.


























































    Table 15.4: A set of test case specifications that cover all pairwise combinations of the possible polymorphic bindings of Account, Credit, and creditCard.


    Open table as spreadsheet

    Account



    Credit



    creditCard



    USAccount



    EduCredit



    VISACard



    USAccount



    BizCredit



    AmExpCard



    USAccount



    individualCredit



    ChipmunkCard



    UKAccount



    EduCredit



    AmExpCard



    UKAccount



    BizCredit



    VISACard



    UKAccount



    individualCredit



    ChipmunkCard



    EUAccount



    EduCredit



    ChipmunkCard



    EUAccount



    BizCredit



    AmExpCard



    EUAccount



    individualCredit



    VISACard



    JPAccount



    EduCredit



    VISACard



    JPAccount



    BizCredit



    ChipmunkCard



    JPAccount



    individualCredit



    AmExpCard



    OtherAccount



    EduCredit



    ChipmunkCard



    OtherAccount



    BizCredit



    VISACard



    OtherAccount



    individualCredit



    AmExpCard



    The combinations in Table 15.4 were of dynamic bindings in a single call. Bindings in a sequence of calls can also interact. Consider, for example, method getYTD- Purchased of class Account shown in Figure 15.4 on page 278, which computes the total yearly purchase associated with one account to determine the applicable discount. Chipmunk offers tiered discounts to customers whose total yearly purchase reaches a threshold, considering all subsidiary accounts.


    The total yearly purchase for an account is computed by method getYTDPurchased, which sums purchases by all customers using the account and all subsidiaries. Amounts are always recorded in the local currency of the account, but getYTDPurchased sums the purchases of subsidiaries even when they use different currencies (e.g., when some are bound to subclass USAccount and others to EUAccount). The intra- and interclass testing techniques presented in the previous section may fail to reveal this type of fault. The problem can be addressed by selecting test cases that cover combinations of polymorphic calls and bindings. To identify sequential combinations of bindings, we must first identify individual polymorphic calls and binding sets, and then select possible sequences.


    Let us consider for simplicity only the method getYTDPurchased. This method is called once for each customer and once for each subsidiary of the account and in both cases can be dynamically bound to methods belonging to any of the subclasses of Account (UKAccount, EUAccount, and so on). At each of these calls, variable totalPurchased is used and changed, and at the end of the method it is used twice more (to set an instance variable and to return a value from the method).


    Data flow analysis may be used to identify potential interactions between possible bindings at a point where a variable is modified and points where the same value is used. Any of the standard data flow testing criteria could be extended to consider each possible method binding at the point of definition and the point of use. For instance, a single definition-use pair becomes n × m pairs if the point of definition can be bound in n ways and the point of use can be bound in m ways. If this is impractical, a weaker but still useful alternative is to vary both bindings independently, which results in m or n pairs (whichever is greater) rather than their product. Note that this weaker criterion would be very likely to reveal the fault in getYTDPurchased, provided the choices of binding at each point are really independent rather than going through the same set of choices in lockstep. In many cases, binding sets are not mutually independent, so the selection of combinations is limited.














    Spring Documentation




















    Spring Documentation



    One of the aspects of Spring that makes it such a useful framework for real developers who are building real applications is its wealth of well-written, accurate documentation. One of the key goals for the 1.1 release was to ensure that all the documentation was finished off and polished by the development team. This means that every feature of Spring is not only fully documented in the JavaDoc, but is also covered in the Spring reference manual included in every distribution.


    If you haven't yet familiarized yourself with the Spring JavaDoc and the reference manual, do so now. This book does not aim to be a replacement for either of these resources; rather, it aims to be a complementary reference, demonstrating how to build a Spring-based application from the ground up.



















    Sharing Functions between Frames











    Sharing Functions between Frames


    One common frame layout uses a permanent navigation frame and a content frame that might display a variety of different pages. Once again, it makes sense to put the call to the external JavaScript file into the page that's always present (the frameset page) instead of duplicating it for every possible content page. In Figure 5.10, we use this capability to have many pages share an identical function that returns a random banner image. Script 5.12 loads the pages into the frameset.




    Figure 5.10. The information in the right, or content, frame is created by code called from the frameset.

    [View full size image]




    Script 5.12. This script allows you to share functions between multiple frames.





    [View full width]
    var bannerArray = new Array("images/redBanner. gif", "images/greenBanner.gif", "images/
    blueBanner.gif");

    window.onload = initFrames;

    function initFrames() {
    var leftWin = document.getElementById ("left").contentWindow.document;

    for (var i=0; i<leftWin.links.length; i++) {
    leftWin.links[i].target = "content";
    leftWin.links[i].onclick = resetBanner;
    }

    setBanner();
    }

    function setBanner() {
    var contentWin = document.getElementById ("content").contentWindow.document;
    var randomNum = Math.floor(Math.random() * bannerArray.length);

    contentWin.getElementById("adBanner").src = bannerArray[randomNum];
    }

    function resetBanner() {
    setTimeout("setBanner()",1000);
    }




    To use a function on another page:












    1.

    [View full width]
    var bannerArray = new Array ("images/redBanner.gif", "images/greenBanner.gif", "images
    /blueBanner.gif");




    Start by creating a new array that contains all the possible banner image names, and assign the array to the bannerArray variable.


    2.

    window.onload = initFrames;




    When the frameset loads, call initFrames().


    3.

    var leftWin = document.getElementById("left"). contentWindow.document;




    Now we start the code inside the initFrames() function. We begin by creating the leftWin variable and setting it the same way we've previously stored framed pages: given the frame name (left), get that element (document.getElementById("left")); given that element, get the contentWindow property (document.getElementById("left").contentWindow); and given the contentWindow property, get its document property.


    4.


    for (var i=0; i<leftWin.links. length; i++) {
    leftWin.links[i].target = "content";
    leftWin.links[i].onclick = resetBanner;




    Because this function is being called from the frameset's context, setting the left navigation bar's links is slightly different than in previous examples. This time, we reset both the target property and the onclick handler for each link. The target is set to "content" and the onclick handler is set to the resetBanner function.


    5.

    setBanner();




    As the last initialization step, the setBanner() function is called.


    6.

    var contentWin = document.getElementById("content"). contentWindow.document;




    The setBanner() function loads up the content window and calculates a random number. Then, the ad banner in the content window is set to a random ad from the array. We begin by creating the contentWin variable and setting it the same way we've previously stored framed pages: given the frame name (content), get that element (document.getElementById("content")); given that element, get the contentWindow property (document.getElementById ("content").contentWindow); and given the contentWindow property, get its document property.


    7.
    var randomNum = Math.floor (Math.random() * bannerArray. length);


    This line uses the Math.random() function multiplied by the number of elements in the bannerArray array to calculate a random number between 0 and the number of elements in the array. Then it places the result into the randomNum variable.


    8.

    contentWin.getElementById ("adBanner").src = bannerArray[randomNum];




    Here, we set the src for adBanner to the current item in the array. That's the new image name, which will then be displayed on the page.


    9.

    function resetBanner() {
    setTimeout("setBanner()",1000);
    }




    The resetBanner() function is a little tricky, although it only has a single line of code. What it's doing is waiting for the content frame to load with its new page (one second should be sufficient), after which it can then call setBanner() to reset the banner.


    If we instead called setBanner() immediately, the new content page might not have loaded yet. In that case, we would have a problem, as we would then either get an error (because adBanner wasn't found), or we would reset the old adBannerthe one from the page that's being unloaded.



    Tip



    • Note that resetBanner does not return falsethis means that the browser will both do what's here and load the page from the HRef. This script depends on that, which is why both the onclick handler and target were set.














    Chapter 6. Disk Arrays










     < Free Open Study > 











    Chapter 6. Disk Arrays





    I/O certainly has been lagging in the last decade.

    �Seymour Cray, 1976





    Most of the improvements in disk technology have been made with the

    aim of increasing the capacity/price ratio. Although these changes

    have made mass storage much more affordable, they have brought about

    two fairly serious problems:





    • When a single disk can store tens of gigabytes of data, the

      reliability of an individual disk becomes a serious concern, as the

      failure of an individual disk results in the loss of large amounts of

      data.

    • Disk performance has lagged drastically behind capacity/price

      improvements.





    To solve these problems, a great deal

    of effort has been put into designing methods of organizing sets of

    disks to enhance both reliability and performance. This has come to

    be known as RAID, which either stands for "Redundant

    Array of Inexpensive Disks" or

    "Redundant Array of Independent

    Disks," depending on who you listen to. There are

    seven levels of RAID; each takes a different approach to solving

    these problems. The types of RAID are summarized in Table 6-1.





    Table 6-1. A summary of RAID levels


    RAID level





    Organization





    Strengths





    Weaknesses





    RAID 0





    Striping





    Very fast, simple





    Low reliability





    RAID 1





    Mirroring





    Fast, simple





    Expensive





    RAID 2





    Reliability viaHamming codes





    Reliable





    Inflexible





    RAID 3





    Parity





    Fast sequential access, reliable





    Slow random access, implementation difficult





    RAID 4





    Parity





    Average performance, reliable





    Bottlenecks on dedicated parity disk





    RAID 5





    Parity





    Good performance, reliable, cheap





    Slow writes, heavy cache requirements





    RAID 10





    Striped mirrors





    Very fast, simple





    Expensive







    There is a fundamental tradeoff in configuring disk arrays -- in

    fact, it is the classical example of the one of the principles of

    performance tuning (see Section 1.2.2). The tradeoff

    is stated in the following note.











    Fast, cheap, safe.



    Of these three attributes, you may pick only two.







    In this chapter, I discuss some of the fundamentals of assembling

    multiple disks into a single logical unit: the basic terminology

    used, the differences between software and hardware disk arrays,

    recipes for desiging disk arrays, and many other aspects of modern

    array implementation.




















       < Free Open Study > 



      Section 9.1.&nbsp; Expected Errors with Calculations









      9.1. Expected Errors with Calculations


      Sometimes, we want to test that the test data is rejected, as expected. For example, consider Figure 9.1, in which a negative amount is used in the first test row.


      Figure 9.1. Negative Amount

      CalculateDiscount

      amount

      discount()

      -100.00

      0.00

      1200.00

      60.00



      When this Fit table is run, we get the report (partly) shown in Figure 9.2, in which the program rejects the negative amount. The report also provides programmer-specific information about the error in the yellow-colored cell (see Plate 5), which we can ignore.



      Figure 9.2. Negative Amount Is Rejected

      [View full size image]




      Assuming that our business rule stipulates that it doesn't make sense to calculate the discount on a negative amount, we'd expect to get an error in that first test row. We can express that expectation by using the special value error in the calculated column instead, as shown in the Fit test in Figure 9.3.


      Figure 9.3. Use of error in ColumnFixture

      CalculateDiscount

      amount

      discount()

      -100.00

      error

      1200.00

      60.00



      The error cell is colored green if an error occurred (an exception), as shown in Figure 9.4. Otherwise, it is colored red.



      Figure 9.4. Negative Amount in error, as Expected







      It makes sense to include the error case here because there is only one. (Fit will complain about any values that are not numbers.) However, many values in a table may not be valid, such as the dates entered by the user through the user interface. In that case, it makes sense to split the table into two: one for defining valid date values and one for defining the calculations on the valid dates. This topic is covered further in Chapter 18.









        8.6 Handling Signals: Errors and Async-signal Safety



        [ Team LiB ]






        8.6 Handling Signals: Errors and Async-signal Safety


        Be aware of three difficulties that can occur when signals interact with function calls. The first concerns whether POSIX functions that are interrupted by signals should be restarted. Another problem occurs when signal handlers call nonreentrant functions. A third problem involves the handling of errors that use errno.


        What happens when a process catches a signal while it is executing a library function? The answer depends on the type of call. Terminal I/O can block the process for an undetermined length of time. There is no limit on how long it takes to get a key value from a keyboard or to read from a pipe. Function calls that perform such operations are sometimes characterized as "slow". Other operations, such as disk I/O, can block for short periods of time. Still others, such as getpid, do not block at all. Neither of these last types is considered to be "slow".


        The slow POSIX calls are the ones that are interrupted by signals. They return when a signal is caught and the signal handler returns. The interrupted function returns �1 with errno set to EINTR. Look in the ERRORS section of the man page to see if a given function can be interrupted by a signal. If a function sets errno and one of the possible values is EINTR, the function can be interrupted. The program must handle this error explicitly and restart the system call if desired. It is not always possible to logically determine which functions fit into this category, so be sure to check the man page.


        It was originally thought that the operating system needs to interrupt slow calls to allow-the user the option of canceling a blocked call. This traditional treatment of handling blocked functions has been found to add unneeded complexity to many programs. The POSIX committee decided that new functions (such as those in the POSIX threads extension) would never set errno to EINTR. However, the behavior of traditional functions such as read and write was not changed. Appendix B gives a restart library of wrappers that restart common interruptible functions such as read and write.


        Recall that a function is async-signal safe if it can be safely called from within a signal handler. Many POSIX library functions are not async-signal safe because they use static data structures, call malloc or free, or use global data structures in a nonreentrant way. Consequently, a single process might not correctly execute concurrent calls to these functions.


        Normally this is not a problem, but signals add concurrency to a program. Since signals occur asynchronously, a process may catch a signal while it is executing a library function. (For example, suppose the program interrupts a strtok call and executes another strtok in the signal handler. What happens when the first call resumes?) You must therefore be careful when calling library functions from inside signal handlers. Table 8.2 lists the functions that POSIX guarantees are safe to call from a signal handler. Notice that functions such as fprintf from the C standard I/O library are not on the list.


        Signal handlers can be entered asynchronously, that is, at any time. Care must be taken so that they do not interfere with error handling in the rest of the program. Suppose a function reports an error by returning -1 and setting errno. What happens if a signal is caught before the error message is printed? If the signal handler calls a function that changes errno, an incorrect error might be reported. As a general rule, signal handlers should save and restore errno if they call functions that might change errno.



        Example 8.28

        The following function can be used as a signal handler. The myhandler saves the value of errno on entry and restores it on return.



        void myhandler(int signo) {
        int esaved;
        esaved = errno;
        write(STDOUT_FILENO, "Got a signal\n", 13);
        errno = esaved;
        }


        Table 8.2. Functions that POSIX guarantees to be async-signal safe.

        _Exit

        getpid

        sigaddset

        _exit

        getppid

        sigdelset

        accept

        getsockname

        sigemptyset

        access

        getsockopt

        sigfillset

        aio_error

        getuid

        sigismember

        aio_return

        kill

        signal

        aio_suspend

        link

        sigpause

        alarm

        listen

        sigpending

        bind

        lseek

        sigprocmask

        cfgetispeed

        lstat

        sigqueue

        cfgetospeed

        mkdir

        sigset

        cfsetispeed

        mkfifo

        sigsuspend

        cfsetospeed

        open

        sleep

        chdir

        pathconf

        socket

        chmod

        pause

        socketpair

        chown

        pipe

        stat

        clock_gettime

        poll

        symlink

        close

        posix_trace_event

        sysconf

        connect

        pselect

        tcdrain

        creat

        raise

        tcflow

        dup

        read

        tcflush

        dup2

        readlink

        tcgetattr

        execle

        recv

        tcgetpgrp

        execve

        recvfrom

        tcsendbreak

        fchmod

        recvmsg

        tcsetattr

        fchown

        rename

        tcsetpgrp

        fcntl

        rmdir

        time

        fdatasync

        select

        timer_getoverrun

        fork

        sem_post

        timer_gettime

        fpathconf

        send

        timer_settime

        fstat

        sendmsg

        times

        fsync

        sendto

        umask

        ftruncate

        setgid

        uname

        getegid

        setpgid

        unlink

        geteuid

        setsid

        utime

        getgid

        setsockopt

        wait

        getgroups

        setuid

        waitpid

        getpeername

        shutdown

        write

        getpgrp

        sigaction

         


        Signal handling is complicated, but here are a few useful rules.


        • When in doubt, explicitly restart library calls within a program or use the restart library of Appendix B.

        • Check each library function used in a signal handler to make sure that it is on the list of async-signal safe functions.

        • Carefully analyze the potential interactions between a signal handler that changes an external variable and other program code that accesses the variable. Block signals to prevent unwanted interactions.

        • Save and restore errno when appropriate.






          [ Team LiB ]