Clover coverage report - clover
Coverage timestamp: Sat Oct 8 2005 22:54:17 EDT
file stats: LOC: 261   Methods: 21
NCLOC: 186   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
AbstractSemanticRecorderFixture.java 77.8% 88.9% 90.5% 86.5%
coverage coverage
 1   
 package abbot.editor.recorder;
 2   
 
 3   
 import java.awt.*;
 4   
 import java.awt.event.*;
 5   
 import java.util.*;
 6   
 
 7   
 import javax.swing.JPopupMenu;
 8   
 
 9   
 import abbot.Log;
 10   
 import abbot.script.*;
 11   
 import abbot.tester.Robot;
 12   
 
 13   
 /**
 14   
  * All SemanticRecorder tests should derive from this class.
 15   
  * Provides a framework similar to EventRecorder which creates appropriate
 16   
  * semantic recorders based on incoming events.<p>
 17   
  * Will continue recording past the first semantic recorder used, preserving
 18   
  * the first one until an invocation of assert[No]Step, which resets the
 19   
  * preserved recorder, replacing it with the current one when appropriate.
 20   
  * This allows a more or less continuous stream of actions alternating with
 21   
  * assert[No]Step calls.<p>
 22   
  * NOTE: it would probably be simpler to not do this and require explicit
 23   
  * startRecording steps, although there is some benefit to checking recorders
 24   
  * back to back (it more closely simulates the EventRecorder behavior).
 25   
  */
 26   
 // FIXME still needs some refactoring
 27   
 // Needs clearer design on when to stop recording and examination of trailing
 28   
 // events that are consumed/not consumed
 29   
 public abstract class AbstractSemanticRecorderFixture
 30   
     extends AbstractRecorderFixture {
 31   
 
 32   
     private ArrayList steps;
 33   
     private Map stepEvents;
 34   
     private SemanticRecorder currentRecorder;
 35   
     private AWTEvent trailingEvent;
 36   
 
 37   
     /** Create a new test case with the given name. */
 38  67
     public AbstractSemanticRecorderFixture(String name) {
 39  67
         super(name);
 40   
     }
 41   
 
 42  67
     protected void fixtureSetUp() throws Throwable {
 43  67
         super.fixtureSetUp();
 44  67
         steps = new ArrayList();
 45  67
         stepEvents = new WeakHashMap();
 46   
     }
 47   
 
 48  67
     protected void fixtureTearDown() throws Throwable {
 49  67
         currentRecorder = null;
 50  67
         trailingEvent = null;
 51  67
         steps.clear();
 52  67
         stepEvents.clear();
 53  67
         super.fixtureTearDown();
 54   
     }
 55   
 
 56   
     /** Each Recorder subclass test should implement this method to return the
 57   
         SemanticRecorder to be tested. */
 58   
     protected abstract SemanticRecorder createSemanticRecorder(Resolver r);
 59   
 
 60  69
     protected void startRecording() {
 61  69
         startRecording(true);
 62   
     }
 63   
 
 64  71
     protected void startRecording(boolean waitForIdle) {
 65   
         // Don't pick up any lingering events
 66  71
         if (waitForIdle)
 67  69
             getRobot().waitForIdle();
 68  71
         trailingEvent = null;
 69  71
         super.startRecording();
 70   
     }
 71   
 
 72   
     private class DefaultRecorder extends Recorder {
 73  71
         public DefaultRecorder(Resolver r) { super(r); }
 74  1674
         protected void recordEvent(AWTEvent event) {
 75  1674
             AbstractSemanticRecorderFixture.this.recordEvent(event);
 76   
         }
 77  0
         protected Step createStep() {
 78  0
             return AbstractSemanticRecorderFixture.this.getStep();
 79   
         }
 80  71
         public long getEventMask() {
 81  71
             return EventRecorder.RECORDING_EVENT_MASK;
 82   
         }
 83  0
         public void terminate() { }
 84   
     }
 85   
 
 86   
     /** Clear the current state of all steps and active recorder. */
 87  71
     protected synchronized void clear() {
 88  71
         currentRecorder = null;
 89  71
         super.clear();
 90  71
         steps.clear();
 91   
     }
 92   
 
 93   
     /** Provide a recorder which acts sort of like EventRecorder, but will
 94   
         always pass events on to the same SemanticRecorder obtained with
 95   
         createRecorder, rather than dynamically determining which recorder to
 96   
         create. */ 
 97  71
     protected synchronized Recorder getRecorder() {
 98  71
         clear();
 99  71
         return new DefaultRecorder(getResolver());
 100   
     }
 101   
 
 102   
     // FIXME is this the best way to handle multiple recorders?
 103  84
     private synchronized void saveCurrentStep() {
 104  84
         if (currentRecorder != null) {
 105  84
             Step step = currentRecorder.getStep();
 106  84
             if (step == null)
 107  11
                 Log.debug("null step!");
 108  84
             if (steps.size() > 0)
 109  10
                 Log.debug("Adding step " + step + " to list of "
 110   
                           + steps.size());
 111  84
             steps.add(step);
 112  84
             stepEvents.put(step, listEvents());
 113  84
             currentRecorder = null;
 114   
         }
 115   
     }
 116   
 
 117   
     // This dispatching is the canonical usage for a semantic recorder; cf
 118   
     // EventRecorder's lookup and dispatch
 119   
     // FIXME maybe make a semantic controller object that both this test and
 120   
     // event recorder use?
 121   
     // FIXME use a different synchronization lock
 122  1682
     protected synchronized void recordEvent(AWTEvent event) {
 123  1682
         if (Boolean.getBoolean("abbot.recorder.log_events"))
 124  0
             Log.log("SREC: " + Robot.toString(event));
 125   
 
 126  1682
         if (currentRecorder == null) {
 127   
             // ordinarily, you'd get an appropriate recorder based on the
 128   
             // event type
 129  549
             SemanticRecorder cr = createSemanticRecorder(getResolver());
 130  549
             if (cr.accept(event)) {
 131  138
                 currentRecorder = cr;
 132  138
                 Log.log("New recorder, event accepted");
 133   
             }
 134   
         }
 135   
 
 136   
         // Don't bother passing on events if the recorder has captured its
 137   
         // target event
 138  1682
         if (currentRecorder != null) {
 139  1271
             SemanticRecorder cr = currentRecorder;
 140  1271
             if (cr.isFinished()) {
 141  0
                 if (trailingEvent == null) {
 142  0
                     trailingEvent = event;
 143  0
                     Log.log("Unconsumed trailing event");
 144   
                 }
 145  0
                 return;
 146   
             }
 147  1271
             boolean consumed;
 148  1271
             boolean finished;
 149  1271
             Log.debug("Recording with " + cr);
 150  1271
             consumed = cr.record(event);
 151  1271
             finished = cr.isFinished();
 152  1271
             if (finished) {
 153  84
                 Log.log("Recorder is finished");
 154  84
                 saveCurrentStep();
 155  84
                 clearEvents();
 156   
             }
 157  1271
             if (!consumed) {
 158  8
                 Log.log("Re-sending event");
 159  8
                 recordEvent(event);
 160   
             }
 161   
         }
 162   
     }
 163   
 
 164   
     /** Verify that no unconsumed events were seen since the last recorded
 165   
         step.
 166   
     */
 167  2
     protected void assertNoTrailingEvents() {
 168  2
         getRobot().waitForIdle();
 169  2
         synchronized(this) {
 170  2
             AWTEvent e = trailingEvent;
 171  2
             trailingEvent = null;
 172  2
             if (e != null) {
 173  0
                 String bugInfo = bugInfo(listEvents());
 174  0
                 fail("Unconsumed trailing events starting at "
 175   
                      + Robot.toString(e) + " " + bugInfo);
 176   
             }
 177   
         }
 178   
     }
 179   
 
 180  12
     protected void assertNoStep() {
 181  12
         getRobot().waitForIdle();
 182  12
         synchronized(this) {
 183  12
             Step step = getStep();
 184  12
             String bugInfo = step != null
 185   
                 ? bugInfo((String)stepEvents.get(step)) : "";
 186  12
             assertTrue("Recorder should not have recorded the step " + step
 187   
                        + " " + bugInfo, step == null);
 188   
         }
 189   
     }
 190   
 
 191   
     /** Make sure the step created matches the given pattern.  The recorder
 192   
      * may not nececessarily have detected the end of the pattern.
 193   
      */
 194  112
     protected void assertStep(String pattern) {
 195  112
         assertStep(pattern, false);
 196   
     }
 197   
 
 198  5
     public void assertStep(String pattern, Step step) {
 199  5
         assertStep(pattern, step, (String)stepEvents.get(step));
 200   
     }
 201   
 
 202   
     /** Make sure the recorder created step matches the given pattern,
 203   
      * optionally requiring the recorder to have detected the end of the
 204   
      * semantic event.
 205   
      */
 206   
     // FIXME this depends on the description that is generated; it should
 207   
     // check the actual step structure instead
 208  112
     protected void assertStep(String pattern, boolean mustBeFinished) {
 209   
         // Make sure all events have been processed
 210  112
         getRobot().waitForIdle();
 211  112
         synchronized(this) {
 212  112
             String bugInfo = bugInfo(listEvents());
 213  112
             Log.log("Verifying recorder state (" + pattern + ")");
 214  112
             try {
 215  112
                 assertTrue("No recorder instantiated " + bugInfo,
 216   
                            currentRecorder != null || steps.size() > 0);
 217  112
                 if (mustBeFinished) {
 218  0
                     assertTrue("Semantic recorder should have detected the "
 219   
                                + "end of this event sequence " + bugInfo, 
 220   
                                (currentRecorder != null
 221   
                                 && currentRecorder.isFinished())
 222   
                                || steps.size() > 0);
 223   
                 }
 224  112
                 Step step = getStep();
 225  112
                 assertStep(pattern, step, (String)stepEvents.get(step));
 226   
             }
 227   
             catch(RuntimeException thr) {
 228  0
                 Log.log("** FAILED ***");
 229  0
                 throw thr;
 230   
             }
 231   
         }
 232   
     }
 233   
 
 234   
     // FIXME this methods purpose is unclear
 235  7
     protected synchronized boolean hasRecorder() {
 236  7
         return currentRecorder != null || steps.size() > 0;
 237   
     }
 238   
 
 239  126
     protected synchronized Step getStep() {
 240  126
         Step step = null;
 241  126
         if (steps.size() > 0) {
 242  74
             Log.log("retrieving saved step");
 243  74
             step = (Step)steps.get(0);
 244  74
             steps.remove(0);
 245   
         }
 246  52
         else if (currentRecorder != null) {
 247  48
             Log.log("asking recorder for step");
 248  48
             step = currentRecorder.getStep();
 249  48
             currentRecorder = null;
 250  48
             if (step != null) {
 251  47
                 stepEvents.put(step, listEvents());
 252  47
                 super.clear();
 253   
             }
 254   
         }
 255   
         else { 
 256  4
             Log.log("No steps and no recorder");
 257   
         }
 258  126
         return step;
 259   
     }
 260   
 }
 261