001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package javax.microedition.lcdui;
028:
029: import com.sun.midp.i3test.TestCase;
030: import com.sun.midp.util.Baton;
031: import com.sun.midp.util.LcduiTestMIDlet;
032: import com.sun.midp.util.LiveTracer;
033: import com.sun.midp.util.LiveTraceListener;
034: import com.sun.midp.util.SerialCallback;
035:
036: /**
037: * Regression tests for CR 6254765. There are actually two bugs mentioned
038: * there. Details are provided for each test case.
039: */
040: public class Test6254765 extends TestCase {
041:
042: Display dpy;
043:
044: /**
045: * Checks all of the itemLFs of the given form to see if their layouts are
046: * valid. Returns true if all are valid, false if any one is invalid.
047: */
048: boolean checkValidLayout(Form form) {
049: FormLFImpl formLF = (FormLFImpl) form.formLF;
050: ItemLFImpl itemLF;
051: boolean anyInvalid = false;
052:
053: for (int ii = 0; ii < formLF.numOfLFs; ii++) {
054: itemLF = formLF.itemLFs[ii];
055: boolean this Invalid = itemLF.actualBoundsInvalid[0]
056: || itemLF.actualBoundsInvalid[1]
057: || itemLF.actualBoundsInvalid[2]
058: || itemLF.actualBoundsInvalid[3];
059: anyInvalid = anyInvalid || this Invalid;
060: // System.out.println(
061: // "item[" + ii + "] bounds (" +
062: // itemLF.bounds[0] + ", " +
063: // itemLF.bounds[1] + ", " +
064: // itemLF.bounds[2] + ", " +
065: // itemLF.bounds[3] + ")" +
066: // (thisInvalid ? " INVALID" : ""));
067: }
068:
069: return !anyInvalid;
070: }
071:
072: /**
073: * Regression test for the main CR described in CR 6254765. This is a
074: * race condition between modifying a Form's contents (for example, with
075: * append) while the Form is in the process of becoming current.
076: */
077: void testScreenChangeAppend() {
078: StringItem items[] = new StringItem[10];
079: final Baton baton = new Baton();
080:
081: for (int ii = 0; ii < items.length; ii++) {
082: items[ii] = new StringItem(null, Integer.toString(ii % 10));
083: }
084:
085: Form form = new Form("Test Form");
086:
087: // gets called from dpy.callScreenChange() after uCallShow()
088: dpy.liveTracer.add(Display.LTR_SCREENCHANGE_AFTERSHOW,
089: new LiveTraceListener() {
090: public void call(String tag) {
091: baton.pass();
092: }
093: });
094:
095: dpy.setCurrent(form);
096:
097: for (int ii = 0; ii < items.length; ii++) {
098: form.append(items[ii]);
099: if (ii == 3) {
100: baton.start();
101: }
102: }
103: baton.finish();
104:
105: // wait for queued events to be processed
106: new SerialCallback(dpy).invokeAndWait();
107:
108: assertTrue("layout must be valid", checkValidLayout(form));
109: dpy.liveTracer.clear();
110: }
111:
112: /**
113: * This is a regression test for another CR that is also mentioned in
114: * 6254765, which occurs when the form is in an inconsistent state such as
115: * what arose from the initial CR, but which can also arise for other
116: * reasons.
117: *
118: * This case is as follows: traverseIndex == -1 and itemTraverse == false,
119: * indicating no initial focus; and getNextInteractiveItem returns a value
120: * >= 0 indicating that there is a focusable item on screen.
121: *
122: * The uTraverse() code assumes that if getNextInteractiveItem returns
123: * some nonnegative value, there must be a currently focused item (that
124: * is, traverseIndex is also nonnegative). However, this is not always the
125: * case. This can occur if the form initially has no visible focusable
126: * items, and the app adds a focusable item, which triggers an invalidate,
127: * and then the user traverses before the invalidate can be processed.
128: * This test simulates that case.
129: */
130: void testTraversalInconsistency() {
131: Form form = new Form("Test Form 2");
132: FormLFImpl formLF = (FormLFImpl) form.formLF;
133: final Baton baton = new Baton();
134: SerialCallback scb = new SerialCallback(dpy);
135:
136: // can be any interactive item
137: Item item = new Gauge(null, true, 1, 0);
138:
139: // Set up a form with no focusable item.
140:
141: form.append("String 1");
142: dpy.setCurrent(form);
143: scb.invokeAndWait();
144:
145: // Block the event queue to prevent the invalidate from
146: // being processed.
147:
148: dpy.callSerially(new Runnable() {
149: public void run() {
150: baton.pass();
151: }
152: });
153: baton.start();
154:
155: // Append a focusable item to the form, and then call
156: // uTraverse() directly, as if a key had been pressed at
157: // exactly the right moment.
158:
159: form.insert(0, item);
160: formLF.uTraverse(Canvas.DOWN);
161: baton.finish();
162:
163: // Wait for the invalidate to finish processing, then
164: // check assertions.
165:
166: scb.invokeAndWait();
167: assertEquals("item 0 should be focused", 0,
168: formLF.traverseIndex);
169: }
170:
171: // main test driver
172:
173: public void runTests() throws Throwable {
174:
175: if (!LcduiTestMIDlet.invoke()) {
176: throw new RuntimeException("can't start LcduiTestMIDlet");
177: }
178:
179: try {
180: dpy = LcduiTestMIDlet.getDisplay();
181:
182: declare("testScreenChangeAppend");
183: testScreenChangeAppend();
184:
185: declare("testTraversalInconsistency");
186: testTraversalInconsistency();
187:
188: } finally {
189: LcduiTestMIDlet.cleanup();
190: }
191: }
192: }
193:
194: /*
195: * A mock LayoutManager. Can be inserted into the LayoutManager class in order
196: * to instrument call to the lLayout method, among other things. To use,
197: * first save away the normal layout manager:
198: *
199: * LayoutManager savedLayoutManager = LayoutManager.instance();
200: *
201: * Then install the mock:
202: *
203: * LayoutManager.singleInstance = new MockLayoutManager();
204: *
205: * After testing, preferably within a finally clause, restore the original
206: * layout manager:
207: *
208: * LayoutManager.singleInstance = savedLayoutManager;
209: *
210: * Failure to do this may have side effects on other tests!
211: */
212:
213: // class MockLayoutManager extends LayoutManager {
214: // int count; // = 0
215: //
216: // void lLayout(int layoutMode,
217: // ItemLFImpl[] itemLFs,
218: // int numOfLFs,
219: // int inp_viewportWidth,
220: // int inp_viewportHeight,
221: // int[] viewable) {
222: // count = numOfLFs;
223: // super.lLayout(
224: // layoutMode,
225: // itemLFs,
226: // numOfLFs,
227: // inp_viewportWidth,
228: // inp_viewportHeight,
229: // viewable);
230: // }
231: // }
|