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 * 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(); 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)); 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. */ ScheduledItem[] preItems = schedule.calDB.getCurrentCalendar().getItemsCopy(); /* * 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. */ validateScheduleEventPostcond(preItems, e); } /** * Run testScheduleEvent case 2. */ protected void testScheduleEventCase2() throws ScheduleEventPrecondViolation { int i; String longString = ""; String longStringReversed = ""; Event e; ScheduledItem[] preItems = schedule.calDB.getCurrentCalendar().getItemsCopy(); 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 )); 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 (i = 3, 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 * 100); if (date.isValid()) { ScheduledItem[] preItems = schedule.calDB.getCurrentCalendar().getItemsCopy(); 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 )); validateScheduleEventPostcond(preItems, e); } } } } p("Done with Cases 3 thru 2564"); } /** * 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() == view && 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(ScheduledItem[] preItems, Event e) { /* * Evaluate the forall with a loop over the array return value of * getItems. Note that this is a "short-circuit" evaluation loop, * where we return false at the first occurrance of postcond violation. * Note also that we are using the value of preItems as the value * represented absractly as \old(...) in the abstract JML logic. */ p("items size = " + schedule.calDB.getCurrentCalendar().getItems().size()); for (ScheduledItem item : schedule.calDB.getCurrentCalendar().getItems()) { p("item = " + item.title); if (! (item.equals(e) || Arrays.asList(preItems).contains(item))) { return false; } p("item in postcond eval loop = " + item.title); } /* ScheduledItem[] itemsArray = null; itemsArray = schedule.calDB.getCurrentCalendar().getItems().toArray(itemsArray); for (int i = 0; i < schedule.calDB.getCurrentCalendar().getItems().size(); i++) { p(i + "th item = " + itemsArray[i]); } */ /* * 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.length + 46) && 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); } }