package caltool.model.schedule; import caltool.model.caldb.*; import mvp.*; import java.util.*; import org.testng.*; import org.testng.annotations.*; /**** * * Class ScheduleTest is the companion testing class for class Schedule . It implements the following module test plan: * */ @Test public class ScheduleTest extends Schedule { /** * Empty constructor, needed to placate the compiler, since parent Schedule * constructor takes two arguments. */ protected ScheduleTest() { super(null, null); } /*-* * Individual unit testing methods for member methods */ /** * Unit test the constructor by building one Schedule object. No further * constructor testing is necessary since only one Schedule object is ever * constructed in the CalendarTool system. I.e., it is a singleton class. *
     *  Test
     *  Case    Input            Output             Remarks
     * ====================================================================
     *   1      null             Proper init done   Only case
     *
     */
    @Test
    protected void testSchedule() {
        schedule = new Schedule(null, new CalendarDB());  // setup & invoke
        Assert.assertTrue(                                // validate
            validateSchedulePostcond(schedule));
    }

    /**
     * Unit test getCategories by calling getCategories on a schedule with a
     * null and non-null categories field.  The Categories model is tested
     * fully in its own class test.
     *                                                                    
     *  Test
     *  Case    Input                   Output          Remarks
     * ====================================================================
     *   1      schedule.categories     null            Null case
     *            == null
     *
     *   2      schedule.categories     same non-null   Non-null case
     *            =- non-null           value
     *                                                                   
*/ @Test(dependsOnMethods = {"testSchedule"}) protected void testGetCategories() { Categories result; // method return value /* * Do case 1 and validate the result. */ schedule.categories = null; // setup result = schedule.getCategories(); // invoke Assert.assertTrue( // validate validateGetCategoriesPostcond(result)); /* * Do case 2 and validate the result. */ schedule.categories = new Categories(); // setup result = schedule.getCategories(); // invoke Assert.assertTrue( // validate validateGetCategoriesPostcond(result)); } /** * Unit test setTaskPriority by sending inputs in and out of range. * * Test * Case Input Output Remarks * ==================================================================== * 1 task, priority = 0 task.priority = 0 Bottom of range * 2 task, priority = 5 task.priority = 5 Middle of range * 3 task, priority = 5 task.priority = 10 Top of range * 4 task, priority = -1 exception thrown Just below bottom * 5 task, priority = -100 exception thrown Well below bottom * 6 task, priority = 11 exception thrown Just above top * 7 task, priority = 100 exception thrown Well above top */ @Test(dependsOnMethods = {"testSchedule"}) void testSetTaskPriority () throws ScheduleTaskPrecondViolation { Task task = new Task(); /* * Check the three presumed successful test cases. */ schedule.setTaskPriority(task, 0); Assert.assertTrue(validateSetTaskPriorityPostcond(task, 0)); schedule.setTaskPriority(task, 5); Assert.assertTrue(validateSetTaskPriorityPostcond(task, 5)); schedule.setTaskPriority(task, 10); Assert.assertTrue(validateSetTaskPriorityPostcond(task, 10)); /* * Check the five presumed exceptional test cases. */ try { schedule.setTaskPriority(task, -1); Assert.assertTrue(false); } catch (ScheduleTaskPrecondViolation e) { Assert.assertTrue(true); } try { schedule.setTaskPriority(task, -100); Assert.assertTrue(false); } catch (ScheduleTaskPrecondViolation e) { Assert.assertTrue(true); } try { schedule.setTaskPriority(task, 11); Assert.assertTrue(false); } catch (ScheduleTaskPrecondViolation e) { Assert.assertTrue(true); } try { schedule.setTaskPriority(task, 100); Assert.assertTrue(false); } catch (ScheduleTaskPrecondViolation e) { Assert.assertTrue(true); } } /** * Unit test scheduleEvent by supplying inputs that test the value range of * each Event data field and exercise each precondition clause. The cases * are as follows: *
     *  Test
     *  Case    Input                   Output          Remarks
     * ====================================================================
     *   1      {"Event 0",             Input event     Lower end of possible
     *           {"Sunday",1,           added to items  event data range
     *            "January",1},         no junk, no
     *            null,                 confusion
     *            null,
     *            null}
     *
     *   2      {10000-char string,     Input event     Upper end of possible
     *           {"Sunday",1,           added to items  event data range
     *            "January",1},         no junk,
     *           {"Saturday",31,        no confusion
     *            "December", 9999},
     *           10000-char string
     *             reversed,
     *           10000-char string}
     *
     *   3      events with start and   Input event     Events with all combos
     *  thru    end date ranging thru   added to items  of days, dates, and
     *  2564    each legal day, each    no junk, no     months
     *          legal date, and each    confusion
     *          legal month, with
     *          rotating year,
     *          category, and security,
     *          where "rotating" means
     *          that test values for
     *          years are generated in
     *          multiples of 100,
     *          numeric-valued category
     *          strings are generated
     *          in multiples of 10,
     *          and the two possible 
     *          values of security are
     *          toggled for each case
     *
     *  2565    32 cases to exercise    Schedule        Events that violate
     *  thru    possible boolean        Event           precond logic.
     *  2597    values for 5 precond    Precond
     *          clauses                 Exception
     *                                   thrown
     *                                                                   
*/ @Test(dependsOnMethods = {"testSchedule"}) protected void testScheduleEvent() { /* * Do the cases. */ try { testScheduleEventCase1(); testScheduleEventCase2(); testScheduleEventCases3Thru2564(); testScheduleEventCases2565Thru2597(); } catch (ScheduleEventPrecondViolation e) { /* This should not happen at this level, but in case it does ... */ System.out.println("scheduleEvent EXCEPTION:"); for (int i = 0; i < e.numberOfErrors(); i++) { System.out.println(e.getErrors()[i]); } } } /** * Run testScheduleEvent case 1. */ protected void testScheduleEventCase1() throws ScheduleEventPrecondViolation { Event e; /* * Get a shallow clone of the pre-scheduling items. */ Collection preItems = schedule.calDB.getCurrentCalendar().getItemsClone(); /* * Call the method under test. */ schedule.scheduleEvent(e = new Event( "Event 0", // title new Date(DayName.Sunday, // start date 1, MonthName.January, 1), null, // end date new Category("Category 1"), // category SimpleSecurity.Public // security )); /* * Do the validation. */ Assert.assertTrue(validateScheduleEventPostcond(preItems, e)); } /** * Run testScheduleEvent case 2. */ protected void testScheduleEventCase2() throws ScheduleEventPrecondViolation { int i; String longString = ""; String longStringReversed = ""; Event e; Collection preItems = schedule.calDB.getCurrentCalendar().getItemsClone(); for (i = 0; i < 10000; i++) { longString += (char) i % 256; longStringReversed = (char) i + longStringReversed; } schedule.scheduleEvent(e = new Event( longString, // title new Date(DayName.Sunday, // start date 1, MonthName.January, 1), new Date(DayName.Saturday, // end date 2, MonthName.December, 9999), new Category(longStringReversed), // category SimpleSecurity.Private // security )); Assert.assertTrue(validateScheduleEventPostcond(preItems, e)); } /** * Run testScheduleEvent cases 3 through 2564 by looping through various * combinations of days, weeks, and months. */ protected void testScheduleEventCases3Thru2564() throws ScheduleEventPrecondViolation { int day, number, month, year, i, n; Date date; Event e; for (day = 0; day < 7; day++) { for (number = 1; number <= 31; number++) { for (month = 0; month < 12; month++) { date = new Date( DayName.values()[day], number, MonthName.values()[month], year = number * 300); if (date.isValid()) { Collection preItems = schedule.calDB.getCurrentCalendar().getItemsClone(); schedule.scheduleEvent(e = new Event( "Event " + // title String.valueOf(n = year + month + day), date, // start date, // end new Category(String.valueOf(n*10)), // category ((number % 2) == 0) ? // security SimpleSecurity.Public : SimpleSecurity.Private )); Assert.assertTrue( validateScheduleEventPostcond(preItems, e)); } } System.out.print("."); // super crude progress "bar" } } } /** * Run testScheduleEvent cases 2565 through 2597 to fully exercise the * precond logic. */ protected void testScheduleEventCases2565Thru2597() { /* Coming next milestone */ } /*-* * Postcondition validation methods. */ /** * Evaluate the Schedule constructor postcond on the given calDB and * cheduleEventPrecondViolation inputs, with this.schedule as the actual * output. */ boolean validateSchedulePostcond(Schedule schedule) { return schedule.getView() == null && schedule.calDB != null && schedule.scheduleEventPrecondViolation != null && !schedule.scheduleEventPrecondViolation.anyErrors(); // 3 other precond checks omitted temporarily for simplicity } /** * Evaluate the getCategories postcond with the given returnVal as the * actual output. */ boolean validateGetCategoriesPostcond(Categories returnVal) { return schedule.categories == returnVal; } /** * Evaluate the setTaskPriority postcond on the given task and priority * value. */ boolean validateSetTaskPriorityPostcond(Task task, int priority) { return task.priority == priority; } /** * Evaluate the scheduleEvet postcond on this.schedule. For quick * reference, here's the postcond: * * (\forall ScheduledItem item; * calDB.getCurrentCalendar().getItems().contains(item); * item.equals(event) || * \old(calDB).getCurrentCalendar().getItems().contains(item)) * * && * * (calDB.getCurrentCalendar().getItems().size() == * \old(calDB).getCurrentCalendar().getItems().size() + 1) * * && * * calDB.getCurrentCalendar().requiresSaving() * * && * * calDB.getCurrentCalendar().hasChanged(); */ boolean validateScheduleEventPostcond( Collection preItems, Event e) { /* * Evaluate the JML \forall with a loop over the Collection returned * UserCalendar.getItems(). Note that this is a "short-circuit" * evaluation loop, where we return false at the first occurrance of a * postcond violation. Note also that we are using the value of * preItems as the value represented absractly as \old(...) in the JML * logic. */ for (ScheduledItem item : schedule.calDB.getCurrentCalendar().getItems()) { if (! (item.equals(e) || preItems.contains(item))) { return false; } } /* * The remaining clauses of the postcond are non-looping bool exprs * that can be evaluated in the normal way. */ return (schedule.calDB.getCurrentCalendar().getItems().size() == preItems.size() + 1) && schedule.calDB.getCurrentCalendar().requiresSaving() && schedule.calDB.getCurrentCalendar().hasChanged(); } /*-* * Testing utility methods. */ /** * Output a header message to stdout identifying the test phase. */ protected void dumpPhaseHeader(int phasenum) { System.out.print("**** Schedule Testing Phase "); System.out.print(phasenum); System.out.println(" ****"); } /** * Output a header message to stdout for the unit test of the given * testname. */ protected void dumpUnitTestHeader(String testname) { System.out.print("** Unit Test " + testname + " **\n"); } /** * Output a header message to stdout for a unit test case. */ protected void dumpUnitTestCaseHeader(String testname, int caseNumber) { System.out.print("* Unit Test Case " + testname + String.valueOf(caseNumber) + " **\n"); } /** * Dump three blank lines following a test phase. */ protected void dumpPhaseEndSpacing() { System.out.print("\n\n\n"); } /** * Dump a couple blank lines following a unit test. */ protected void dumpUnitTestEndSpacing() { System.out.print("\n\n"); } /** * Dump just a string message. */ protected void dumpMessage(String message) { System.out.println(message); } /** * Dump the data values of schedule to stdout. For test validation * purposes, include a print of the number of elements in the dumped items. * Precede the dump with the given message, if the message is non-null. */ protected void dump(String message) { int l; // Temp if (message != null) { System.out.print("* " + message + " *" + "\n"); } System.out.print("Schedule contains\n" + "Categories: " + schedule.categories + "\n" + (l = schedule.calDB.getCurrentCalendar().numItems()) + " " + (l == 1 ? " item" : " items") + "\n" + schedule.toString() + "\n" ); } /** * Schedule data object to support the tests. */ protected Schedule schedule; /** * Unit test scheduleAppointment using test cases 1-2564 of * testScheduleEvent, with the addition of rotating values for the * components of an Appointment that aren't in an Event. These rotating * vales are: toggling value for recurring, toggling value for Priority, * toggling value for remind, string value of details that starts with * empty and increments by string length of 1, with the addition of of * numerically increasing ASCII character values. * * 2565 32 cases to exercise Schedule Appointments that * thru possible boolean Apppointment violate precond logic. * 2629 values for 6 precond Precond * clauses Exception * thrown * */ protected void testScheduleAppointment() { } /** * Unit test scheduleTask using test cases 1-2564 of * testScheduleAppointment, disregarding rotating values for the components * of an Appointment that aren't in a Task, and adding rotating values for * the Due Time and Priority components of a Task that are not in an * Appointment. Values for Due Time rotate from 00:00 through 23:50 in * increments of 10 minutes (i.e., modulo 24:00). Values for Priority * rotate from 0 through 10 (i.e., modulo 11). */ protected void testScheduleTask() { } private void p(String s) { System.out.println(s); } }