NivaarExam PrepOfficial exam papers ↗

19-Soft-A6 Software Quality Assurance · December 2013

Question 6 of 8: Basis Path Testing of the Appendix A Property Database

Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)

Notes on this paper

National Exams, December 2013 — 04-Soft-A6, Software Quality Assurance (open book, non-communicating calculator permitted, 3 hours). Per the paper's own notes, FIVE of the EIGHT questions constitute a complete exam and each is of equal value; all eight are answered in full below as a complete study resource.

Reference texts. Pressman, Software Engineering: A Practitioner's Approach, 9th ed., Ch. 15 (SQA), Ch. 17–18 (unit/integration/validation/system testing strategy), Ch. 19–20 (white-box basis-path testing, black-box equivalence partitioning & boundary value analysis); Sommerville, Software Engineering, 10th ed., Ch. 8 (Software Testing) and Ch. 24 (Quality Management); SWEBOK v4, Software Quality KA and Software Testing KA; ISO/IEC 25010 (SQuaRE) for the software product quality model referenced in Question 1; ISO/IEC/IEEE 12207 (Software life cycle processes) for the process-standard referenced in Question 1(b).

Question 6: Basis Path Testing of the Appendix A Property Database (10 marks)

Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.

Appendix A (as printed in the exam) is reproduced below for reference:

// A simple in-memory property database program. Through a menu, the user
// decides if he or she wants to see a property database on the screen or
// search for a specific property.
#include <iostream.h>
#include <string.h>               // For strcmp()

void DisplayMenu();
int  GetAnswer();
void DisplayProps(char * code[], float price[], char * addr[], float commPer[]);
void SearchProps (char * code[], float price[], char * addr[], float commPer[]);

int const NUM = 8;                // Eight properties maximum

void main()
{
    int ans;
    char * code[NUM]    = { "231DV", "821WQ", "199OI", "294JU",
                             "901RE", "829BN", "483LQ", "778AS" };
    float price[NUM]    = { 89432.34, 123029.34, 321293.95, 214293.20,
                             68402.92, 421034.53, 232456.54, 432123.40 };
    char * addr[NUM]    = { "919 N. Elm", "2202 West Sycamore", "7560 E. 26th Pl.",
                             "213 W. 104th Ave", "123 Willow Rd.", "5629 S. 188th",
                             "45 North Harvard", "17093 Lansford" };
    float commPer[NUM]  = { .072, .07, .065, .091, .078, .0564, .102, .0834 };

    do
    {
        DisplayMenu();
        ans = GetAnswer();
        switch (ans)
        {
            case (1): { DisplayProps(code, price, addr, commPer); break; }
            case (2): { SearchProps (code, price, addr, commPer); break; }
            case (3): { return; break; }              // "unreachable code"
        }               // If user entered bad value, while loop will repeat
    } while (ans != 3);
    return;
}
//--------------------------------------------------------------------------
void DisplayMenu() { /* prints the 3-choice menu; no branch */ }
//--------------------------------------------------------------------------
int GetAnswer() { int ans; cin >> ans; cin.ignore(80,'\n'); return (ans); }
//--------------------------------------------------------------------------
void DisplayProps(char * code[], float price[], char * addr[], float commPer[])
{
    int ctr;
    cout.precision(2); cout.setf(ios::showpoint); cout.setf(ios::fixed);
    for (ctr = 0; ctr < NUM; ctr++)
    {
        cout << "Code: " << code[ctr] << "\t Price: $" << price[ctr] << endl;
        cout << "Address: " << addr[ctr] << endl;
        cout << "Commission percentage: " << commPer[ctr]*100.0 << "%" << endl << endl;
        if (ctr == 3)                                   // Don't scroll off too fast
        {
            cout << "Press enter to continue..."; cin.ignore(80,'\n');
        }
    }
    cout << "Press enter to continue..."; cin.ignore(80,'\n');
    return;
}
//--------------------------------------------------------------------------
void SearchProps(char * code[], float price[], char * addr[], float commPer[])
{
    int ctr; int found = 0; char buf[6];
    cout << "What is the property's code? "; cin.getline(buf, 6);
    for (ctr = 0; ctr < NUM; ctr++)
    {
        if (!strcmp(code[ctr], buf))
        {
            cout << "Code: " << code[ctr] << "\t Price: $" << price[ctr] << endl;
            cout << "Address: " << addr[ctr] << endl;
            cout << "Commission percentage: " << commPer[ctr]*100.0 << "%" << endl << endl;
            found = 1;
            break;
        }
    }
    if (!found) { cout << "* I'm sorry, but I don't find code " << buf; }
    return;
}

Find. The control-flow graph, cyclomatic complexity V(G), and a basis (minimum, independent) set of test cases for each unit in Appendix A, ordered by the program's call hierarchy as the hint directs.

main()V(G) = 5DisplayMenu()V(G) = 1GetAnswer()V(G) = 1DisplayProps()V(G) = 3SearchProps()V(G) = 4call (open-headed arrow = data-coupled call, per structure-chart notation)
Fig. Q6-1 — Call hierarchy of Appendix A with each unit's cyclomatic complexity. The hint's "focus on the functions' hierarchy" means test the four leaf/near-leaf units bottom-up FIRST (each with a driver, per Question 3(b)), then test main() last, once real (already-tested) units are available to replace what would otherwise need stubs.
  1. Order testing by the hierarchy. DisplayMenu() and GetAnswer() are leaves with no decision logic (V(G) = 1 each) — a single driver call each is sufficient, since with only one path there is nothing further to distinguish. DisplayProps() and SearchProps() are also leaves (they call only library I/O) but contain real decision logic, so each needs its own basis-path test set, built with a driver, before main() is tested. main() is tested last, calling the four real, already-verified units directly (bottom-up integration, Question 3(b)) rather than stubs.
  2. Build each unit's flow graph and count V(G) = E − N + 2. Nodes are numbered at each statement/decision. DisplayProps(): 1 start → 2 for-test → 3 loop body(print) → 4 if(ctr==3) → 5 if-true(pause) → 6 merge/increment → 7 exit, with edges {1-2, 2-3, 2-7, 3-4, 4-5, 4-6, 5-6, 6-2}: N=7, E=8, $V(G) = 8-7+2 = 3$. SearchProps(): 1 start → 2 for-test → 3 if(!strcmp) → 4 match/break → 5 no-match/increment → 6 if(!found) → 7 not-found body → 8 exit, with edges {1-2, 2-3, 2-6, 3-4, 3-5, 5-2, 4-6, 6-7, 6-8, 7-8}: N=8, E=10, $V(G) = 10-8+2 = 4$ (shown in Fig. Q6-2).
  3. Build main()'s flow graph, including the un-defaulted switch's implicit branch. Nodes: 1 start/declarations → 2 DisplayMenu() call → 3 GetAnswer() call → 4 switch(ans) → 5 case 1/DisplayProps → 6 case 2/SearchProps → 7 case 3/return → 8 merge → 9 while(ans!=3) test → 10 exit. The switch has no default, so its own comment ("If user entered bad value, while loop will repeat") describes a real fourth edge, $4\to 8$, taken whenever ans matches none of 1/2/3 and execution falls through the switch untouched. Edges: {1-2, 2-3, 3-4, 4-5, 4-6, 4-7, 4-8, 5-8, 6-8, 7-10, 8-9, 9-2, 9-10}: N=10, E=13, $V(G) = 13-10+2 = 5$.
  4. Cross-check by the predicate-node formula $V(G) = 1+\sum(\text{out-degree}(p)-1)$ over every decision node $p$. main(): switch (4-way, contributes 3) + while-test (2-way, contributes 1) $= 1+3+1=5$ — matches. SearchProps(): three 2-way decisions (for-test, strcmp-test, found-test) $=1+1+1+1=4$ — matches. DisplayProps(): two 2-way decisions $=1+1+1=3$ — matches. Both formulas agree for every unit, which is the standard cross-check basis path testing uses before trusting a hand-drawn graph.
1 decls + prompt +getline(buf)2 for(ctr=0;ctr<NUM;ctr++)3 if(!strcmp(code[ctr],buf))4 print match;found=1; break5 (no match,ctr++)6 if(!found)7 print"not found"8 returnTFTFTFPredicate nodes (2, 3, 6) outlined in blue -- N=8, E=10, V(G)=E-N+2=4
Fig. Q6-2 — McCabe flow graph for SearchProps(). Predicate (decision) nodes 2, 3, and 6 are outlined in blue; each contributes one independent path beyond the baseline.
UnitV(G)Basis test-case set (V(G) cases, collectively covering every edge)
DisplayMenu()1Single call — confirm the 3-choice menu text prints once; no branch to isolate.
GetAnswer()1Single call with any integer input — confirm the same value is echoed back by the driver; no branch to isolate.
DisplayProps()3(1) Normal pass: all 8 records print, output pauses exactly once at ctr==3 — exercises the loop's T edge and the if's T edge. (2) Force NUM = 0 in the driver's stub array: loop body never runs — exercises the loop's F edge directly (else the loop always executes at least once with the real NUM = 8). (3) Force the pause index out of range (e.g. a driver-supplied array where no ctr ever equals 3, by using NUM ≤ 3): confirms the if's F edge on every iteration with no pause — a genuine defect this catches is a hard-coded pause index (3) that silently stops pausing at all if the property count ever drops to 3 or fewer.
SearchProps()4(1) Code found on the FIRST comparison (buf = "231DV"): shortest path, exercises the break-out edge directly. (2) Code NOT found anywhere (buf = "ZZZZZ"): loop runs all 8 iterations to natural exhaustion, exercises the loop's own F edge and the "not found" message. (3) Code found in the MIDDLE (buf = "901RE", index 4): confirms the strcmp test's F edge is taken (and re-entered) at least once before its T edge fires — distinct from cases (1)/(2)'s degenerate zero/eight repeats. (4) Code found on the LAST element (buf = "778AS", index 7 = NUM−1): the boundary case most likely to expose an off-by-one in ctr<NUM vs. ctr<=NUM.
main()5(1) ans = 1 then 3 ("view", quit): exercises case 1 and the loop-back edge. (2) ans = 2 then 3 ("search", quit): exercises case 2. (3) ans = 3 immediately: exercises case 3's direct-return edge, the shortest possible path. (4) ans = 9 (out of range) then 3: exercises the un-defaulted switch's implicit fall-through edge $4\to 8$, i.e. checks the "bad value, loop will repeat" comment is actually true rather than a stale assumption. (5) ans = 1, 1, then 3 (repeat the same valid choice twice before quitting): confirms the loop-back edge is safely repeatable, not just single-shot — the kind of multi-iteration check that basis path testing alone does not guarantee and Question 4(a)'s separate loop-testing technique specifically targets.