/*  TestCases.java
 *  Geoff Oakham (oakhamg@cs)
 *  Oct 26, 2001
 */

/* 148.a2 tester, Nick Chang */

import java.lang.reflect.Modifier;
import java.io.*;
import java.util.*;
import java.awt.Rectangle;

// A main method to autotest LinkedBubble.sort().
// All test cases use the MyInt class and are hard-coded (no input required).
class TestCases {
  
  static public int TOTAL_TESTS = 48;
  static public int FREE_MARKS = 0;
  
  private StructureChecker sc;
  private final String[] FILE= {"DoublingWindow", "ResizingWindow", "QuarteringWindow",
    "DayNameWindow", "StairWindow", "OverlappingWindow"};
  private Logger logger;
  
  public TestCases(Logger logger) {
    this.logger = logger;
    this.sc= new StructureChecker(logger);
  }
  
  /** Performs test number "testNumber" and returns true if the testcase
   * succeeds.  The caller takes responsibility for any stray exceptions
   * thrown during testing.  These exceptions will be recorded and logged
   * appropriately (ie, as failed testcases.)  A test case that excercises
   * code that's supposed to throw exceptions, should catch those
   * exceptions and report their testcases as 'passed'.
   *
   * NOTE: it is important the caller receive all thrown threading-related
   * exceptions, including 'threaddeath' and 'interupt'.  Should you decide
   * to catch stray exceptions yourself please keep this in mind.  Please
   * also consider modifying the upstream code to meet your needs instead.
   *
   * Preconditions: 0 <= testNumber < TOTAL_TESTS,
   *   and runTest(i) for i < testNumber has previously been called (once
   *   each.)
   */
  public boolean runTest(int testNumber) throws Exception {
    boolean r = true;

    final int WIDTH= 200;
    final int HEIGHT= 300; 
    final int X= 250;
    final int Y= 350;
    
    int violationCount= 0;
    DoublingWindow dw2= null;
    ResizingWindow rw= null;
    QuarteringWindow qw= null;
    DayNameWindow dnw= null;
    StairWindow sw= null;
    OverlappingWindow ow= null;
    Date date;
    Day day;
    Rectangle r1, r2, r3, r4;

    switch (testNumber) {
      case 0:
        // does doubleSize double the size?
        dw2= new DoublingWindow();
        dw2.setSize(WIDTH, HEIGHT);
        dw2.setLocation(X, Y);
        dw2.doubleSize();
        r= r && dw2.getWidth() == WIDTH*2 &&
          dw2.getHeight() == HEIGHT*2;
        break;
        
      case 1:
        // does doubleSize change the location (it shouldn't)?
        dw2= new DoublingWindow();
        dw2.setSize(WIDTH, HEIGHT);
        dw2.setLocation(X, Y);
        dw2.doubleSize();
        r= r && dw2.getX() == X && dw2.getY() == Y;
        break;
        
      case 2:
        // does ResizingWindow.shrink(int, int) work?
        rw= new ResizingWindow();
        rw.setSize(WIDTH, HEIGHT);
        rw.setLocation(X, Y);
        rw.shrink(35, 25);
        r= r && rw.getWidth() == WIDTH - 35 &&
          rw.getHeight() == HEIGHT - 25;
        break;
        
      case 3:
        // does ResizingWindow.shrink(int, int) change the location (it shouldn't)?
        rw= new ResizingWindow();
        rw.setSize(WIDTH, HEIGHT);
        rw.setLocation(X, Y);
        rw.shrink(35, 25);
        r= r &&
          rw.getX() == X && rw.getY() == Y;
        break;
        
      case 4:
        // does ResizingWindow.resize(double) work for p > 1?
        rw= new ResizingWindow();
        rw.setSize(WIDTH, HEIGHT);
        rw.setLocation(X, Y);
        rw.resize(3.1415);
        r= r &&
          rw.getWidth() == (int) (WIDTH * 3.1415) &&
          rw.getHeight() == (int) (HEIGHT * 3.1415);
        break;
        
      case 5:
        // does ResizingWindow.resize change the location (better not)?
        rw= new ResizingWindow();
        rw.setSize(WIDTH, HEIGHT);
        rw.setLocation(X, Y);
        rw.resize(3.1415);
        r= r &&
          rw.getX() == X && rw.getY() == Y;
        break;
        
      case 6:
        // does ResizingWindow.resize(double) work for p < 1?
        rw= new ResizingWindow();
        rw.setSize(WIDTH, HEIGHT);
        rw.setLocation(X, Y);
        rw.resize(0.4148);
        r= r &&
          rw.getWidth() == (int) (WIDTH * 0.4148) &&
          rw.getHeight() == (int) (HEIGHT * 0.4148);
        break;
        
      case 7:
        // does ResizingWindow.resize change the location (better not!)
        rw= new ResizingWindow();
        rw.setSize(WIDTH, HEIGHT);
        rw.setLocation(X, Y);
        rw.resize(0.4148);
        r= r &&
          rw.getX() == X && rw.getY() == Y;
        break;
        
      case 8:
        // does ResizingWindow.getArea() work
        rw= new ResizingWindow();
        rw.setSize(WIDTH, HEIGHT);
        rw.setLocation(X, Y);
        r= r && rw.getArea() == WIDTH * HEIGHT;
        break;
        
      case 9:
        // does ResizingWindow.getArea() change size (better not!)
        rw= new ResizingWindow();
        rw.setSize(WIDTH, HEIGHT);
        rw.setLocation(X, Y);
        rw.getArea(); // unused return value
        r= r &&
          rw.getWidth() == WIDTH &&
          rw.getHeight() == HEIGHT;
        break;
        
      case 10:
        // does ResizingWindow.getArea() change location (better not!)
        rw= new ResizingWindow();
        rw.setSize(WIDTH, HEIGHT);
        rw.setLocation(X, Y);
        rw.getArea(); // unused return value
        r= r &&
          rw.getX() == X && rw.getY() == Y;
        break;
        
      case 11:
        // does DayNameWindow.displayToday() work?
        dnw= new DayNameWindow();
        date= new Date();
        day= new Day(date.getDay());
        dnw.setDateAndDay();
        dnw.displayToday();
        r= r &&
          dnw.getTitle().equals(day.today());
        break;
        
      case 12:
        // does DayNameWindow.displayTomorrow() work?
        dnw= new DayNameWindow();
        date= new Date();
        day= new Day(date.getDay());
        dnw.setDateAndDay();
        dnw.displayTomorrow();
        r= r &&
          dnw.getTitle().equals(day.tomorrow());
        break;
        
      case 13:
        // does DayNameWindow.displayYesterday() work?
        dnw= new DayNameWindow();
        date= new Date();
        day= new Day(date.getDay());
        dnw.setDateAndDay();
        dnw.displayYesterday();
        r= r &&
          dnw.getTitle().equals(day.yesterday());
        break;
        
      case 14:
        // does DayNameWindow.displayToday() change size or location?
        // better not!
        dnw= new DayNameWindow();
        dnw.setSize(WIDTH, HEIGHT);
        dnw.setLocation(X, Y);
        date= new Date();
        day= new Day(date.getDay());
        dnw.setDateAndDay();
        dnw.displayYesterday();
        r= r &&
          dnw.getHeight() == HEIGHT &&
          dnw.getWidth() == WIDTH &&
          dnw.getX() == X &&
          dnw.getY() == Y;
        break;
        
      case 15:
        // does QuarteringWindow.quarter() work?
        qw= new QuarteringWindow();
        qw.setSize(WIDTH, HEIGHT);
        qw.setLocation(X, Y);
        qw.clearShowList();
        qw.quarter();
        r1= new Rectangle(X, Y, WIDTH/2, HEIGHT/2);
        r2= new Rectangle(X + WIDTH/2, Y, WIDTH/2, HEIGHT/2);
        r3= new Rectangle(X, Y + HEIGHT/2, WIDTH/2, HEIGHT/2);
        r4= new Rectangle(X + WIDTH/2, Y + HEIGHT/2,
                          WIDTH/2, HEIGHT/2); 
        r= r &&
          qw.rectInShowList(r1) &&
          qw.rectInShowList(r2) &&
          qw.rectInShowList(r4);
        break;
        
      case 16:
        // does QuarteringWindow.quarterVertical work?
        qw= new QuarteringWindow();
        qw.setSize(WIDTH, HEIGHT); 
        qw.setLocation(X, Y);
        qw.clearShowList();
        qw.quarterVertical();
        r1= new Rectangle(X, Y, WIDTH/4, HEIGHT);
        r2= new Rectangle(X + WIDTH/4, Y, WIDTH/4, HEIGHT);
        r3= new Rectangle(X+ 2 * WIDTH / 4, Y, WIDTH/4, HEIGHT);
        r4= new Rectangle(X + 3 * WIDTH / 4, Y, WIDTH/4, HEIGHT);
        r= r &&
          qw.rectInShowList(r1) &&
          qw.rectInShowList(r2) &&
          qw.rectInShowList(r3) &&
          qw.rectInShowList(r4);
        break;
        
      case 17:
        // does QuarteringWindow.quarterHorizontal work?
        qw= new QuarteringWindow();
        qw.setSize(WIDTH, HEIGHT); //want multiples of 4
        qw.setLocation(X, Y);
        qw.clearShowList();
        qw.quarterHorizontal();
        r1= new Rectangle(X, Y, WIDTH, (HEIGHT)/4);
        r2= new Rectangle(X, Y + HEIGHT/4, WIDTH, HEIGHT/4);
        r3= new Rectangle(X, Y + HEIGHT/2, WIDTH, HEIGHT/4);
        r4= new Rectangle(X, Y + 3 * HEIGHT / 4, WIDTH, HEIGHT/4);
        r= r &&
          qw.rectInShowList(r1) &&
          qw.rectInShowList(r2) &&
          qw.rectInShowList(r3) &&
          qw.rectInShowList(r4);
        break;
        
      case 18:
        // does QuarteringWindow.quarter() change size or location (shouldn't)
        qw= new QuarteringWindow();
        qw.setSize(WIDTH, HEIGHT);
        qw.setLocation(X, Y);
        qw.quarter();
        r= r &&
          qw.getWidth() == WIDTH && qw. getHeight() == HEIGHT &&
          qw.getX() == X && qw.getY() == Y;
        break;
        
      case 19:
        // does QuarteringWindow.quarterHorizontal() 
        // change size or location (shouldn't)
        qw= new QuarteringWindow();
        qw.setSize(WIDTH, HEIGHT);
        qw.setLocation(X, Y);
        qw.quarterHorizontal();
        r= r &&
          qw.getWidth() == WIDTH && qw. getHeight() == HEIGHT &&
          qw.getX() == X && qw.getY() == Y;
        break;
        
      case 20:
        // does QuarteringWindow.quarterVertical() 
        // change size or location (shouldn't)
        qw= new QuarteringWindow();
        qw.setSize(WIDTH, HEIGHT);
        qw.setLocation(X, Y);
        qw.quarterVertical();
        r= r &&
          qw.getWidth() == WIDTH && qw. getHeight() == HEIGHT &&
          qw.getX() == X && qw.getY() == Y;
        break;
        
      case 21:
        // does StairWindow.up() work?
        sw= new StairWindow();
        sw.setSize(WIDTH, HEIGHT);
        sw.setLocation(X, Y);
        sw.clearShowList();
        sw.up();
        r1= new Rectangle(X + WIDTH, Y - HEIGHT, WIDTH, HEIGHT);
        r2= new Rectangle(X + 2 * WIDTH, Y - 2 * HEIGHT, WIDTH, HEIGHT);
        r3= new Rectangle(X + 3 * WIDTH, Y - 3 * HEIGHT, WIDTH, HEIGHT);
        r= r &&
          sw.rectInShowList(r1) && sw.rectInShowList(r2) &&
          sw.rectInShowList(r3);
        break;
        
      case 22:
        // does StairWindow.up() change location or size of
        // original (it shouldn't)
        sw= new StairWindow();
        sw.setSize(WIDTH, HEIGHT);
        sw.setLocation(X, Y);
        sw.up();
        r= r &&
          sw.getWidth() == WIDTH && sw. getHeight() == HEIGHT &&
          sw.getX() == X && sw.getY() == Y;
        break;
        
      case 23:
        // does StairWindow.down() work?
        sw= new StairWindow();
        sw.setSize(WIDTH, HEIGHT);
        sw.setLocation(X, Y);
        sw.clearShowList();
        sw.down();
        r1= new Rectangle(X + WIDTH, Y + HEIGHT, WIDTH, HEIGHT);
        r2= new Rectangle(X + 2 * WIDTH, Y + 2 * HEIGHT, WIDTH, HEIGHT);
        r3= new Rectangle(X + 3 * WIDTH, Y + 3 * HEIGHT, WIDTH, HEIGHT);
        r= r &&
          sw.rectInShowList(r1) && sw.rectInShowList(r2) &&
          sw.rectInShowList(r3);
        break;
        
      case 24:
        // does StairWindow.down() change location or size of
        // original (it shouldn't)
        sw= new StairWindow();
        sw.setSize(WIDTH, HEIGHT);
        sw.setLocation(X, Y);
        sw.down();
        r= r &&
          sw.getWidth() == WIDTH && sw. getHeight() == HEIGHT &&
          sw.getX() == X && sw.getY() == Y;
        break;
        
      case 25:
        // does OverlappingWindow.overlap() work with positive offsets?
        ow= new OverlappingWindow();
        ow.setSize(WIDTH, HEIGHT);
        ow.setLocation(X, Y);
        ow.clearShowList();
        ow.setOverlap(50,100);
        ow.overlap();
        r1= new Rectangle(X + 50, Y + 100, WIDTH, HEIGHT);
        r= r &&
          ow.rectInShowList(r1);
        break;
        
      case 26:
        // does OverlappingWindow.overlap() change size or location?
        // (it shouldn't)
        ow= new OverlappingWindow();
        ow.setSize(WIDTH, HEIGHT);
        ow.setLocation(X, Y);
        ow.setOverlap(50,100);
        ow.overlap();
        r= r &&
          ow.getWidth() == WIDTH && ow.getHeight() == HEIGHT &&
          ow.getX() == X && ow.getY() == Y;
        break;
        
      case 27:
        // does OverlappingWindow.overlap() work with negative offsets?
        ow= new OverlappingWindow();
        ow.setSize(WIDTH, HEIGHT);
        ow.setLocation(X, Y);
        ow.clearShowList();
        ow.setOverlap(-50,-100);
        ow.overlap();
        r1= new Rectangle(X - 50, Y - 100, WIDTH, HEIGHT);
        r= r &&
          ow.rectInShowList(r1);
        break;
        
      case 28:
        // Fewer than two uses of static
        r= r && staticBelow(2, 28);
        break;
        
      case 29:
        // Fewer than three uses of static
        r= r && staticBelow(3, 29);
        break;
        
      case 30:
        // Fewer than four uses of static
        r= r && staticBelow(4, 30);
        break;
        
      case 31:
        // Fewer than five uses of static
        r= r && staticBelow(5, 31);
        break;
        
      case 32:
        // Fewer than two private methods or public fields
        r= r && privateMethodPublicFieldBelow(2, 32);
        break;
        
      case 33:
        // Fewer than three private methods or public fields
        r= r && privateMethodPublicFieldBelow(3, 33);
        break;
        
      case 34:
        // Fewer than four private methods or public fields
        r= r && privateMethodPublicFieldBelow(4, 34);
        break;
        
      case 35:
        // Fewer than five private methods or public fields
        r= r && privateMethodPublicFieldBelow(5, 35);
        break;
        
      case 36:
        // Fewer than two bad capitalizations
        r= r && badCapitalizationBelow(2, 36);
        break;
        
      case 37:
        // Fewer than three bad capitalizations
        r= r && badCapitalizationBelow(3, 37);
        break;
        
      case 38:
        // Fewer than four bad capitalizations
        r= r && badCapitalizationBelow(4, 38);
        break;
        
      case 39:
        // Fewer than five bad capitalizations
        r= r && badCapitalizationBelow(5, 39);
        break;
        
      case 40:
        // Fewer than two loops or ifs
        r= r && loopIfBelow(2, 40);
        break;
        
      case 41:
        // Fewer than three loops or ifs
        r= r && loopIfBelow(3, 41);
        break;
        
      case 42:
        // Fewer than four loops or ifs
        r= r && loopIfBelow(4, 42);
        break;
        
      case 43:
        // Fewer than five loops or ifs
        r= r && loopIfBelow(5, 43);
        break;
        
      case 44:
        // Fewer than two prints
        r= r && printBelow(2, 44);
        break;
        
      case 45:
        // Fewer than three prints
        r= r && printBelow(3, 45);
        break;
        
      case 46:
        // Fewer than four prints
        r= r && printBelow(4, 46);
        break;
        
      case 47:
        // Fewer than five prints
        r= r && printBelow(5, 47);
        break;
        
      default:
        throw new java.lang.Error();
    }
    
    return r;
  }

  /**
   * check whether the number of uses of static is less
   * than threshold, log to test case number testCase.
   */
  private boolean staticBelow(int threshold, 
                              int testCase) throws Exception {
    int violationCount= 0;
    for (int i= 0; i < FILE.length; i++) {
      violationCount+= 
        sc.badModifierCount(FILE[i], Modifier.STATIC, "field",
                            "static field found in ", testCase);
    }
        
    for (int i= 0; i < FILE.length; i++) {
      violationCount+= 
        sc.badModifierCount(FILE[i], Modifier.STATIC, "method",
                            "static method found in ", testCase);
    }
    return violationCount < threshold;
  }
  
  /**
   * check whether the number of private methods or
   * public fields exceeds threshold, log to test case
   * number testCase.
   */
  private boolean privateMethodPublicFieldBelow(int threshold, 
                                                int testCase) throws Exception {
    int violationCount= 0;
    for (int i= 0; i < FILE.length; i++) {
      violationCount+= 
        sc.badModifierCount(FILE[i], Modifier.PRIVATE, "method",
                            "private method found in ", testCase);
    }
    for (int i= 0; i < FILE.length; i++) {
      violationCount+=
        sc.badModifierCount(FILE[i], Modifier.PUBLIC, "field",
                            "public field found in ", testCase);
    }
    return violationCount < threshold;
  }
  
  /**
   * check whether the number of bad capitalizations exceeds
   * threshold, log to test case number testCase.
   */
  private boolean badCapitalizationBelow(int threshold, 
                                         int testCase) throws Exception {
    int violationCount= 0;
    for (int i= 0; i < FILE.length; i++) {
      violationCount+=
        sc.badCapitalizationCount(FILE[i],
                                  "Bad capitalization: ", testCase);
    }
    return violationCount < threshold;
  }
  
  /**
   * check whether the number of print statements exceeds
   * threshold, log to test case number testCase.
   */
  private boolean printBelow(int threshold,
                             int testCase) throws Exception {
    int violationCount= 0;
    for (int i= 0; i < FILE.length; i++) {
      violationCount += 
        sc.badPrintCount(FILE[i], testCase);
    }
    return violationCount < threshold;
  }
  
  /**
   * check whether the number of loops or if statements exceeds
   * threshold, log to test case number testCase
   */
  private boolean loopIfBelow(int threshold,
                              int testCase) throws Exception {
    int violationCount= 0;
    for (int i= 0; i < FILE.length; i++) {
      violationCount+= sc.badLoopIfCount(FILE[i], testCase);
    }
    return violationCount < threshold;
  }
}

