1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *
15 *  See the License for the specific language governing permissions and
16 *  limitations under the License.
17 */
18
19/**
20 * @author Vitaly A. Provodin
21 */
22
23/**
24 * Created on 29.01.2005
25 */
26package org.apache.harmony.jpda.tests.jdwp.share;
27
28import org.apache.harmony.jpda.tests.framework.TestErrorException;
29import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
30import org.apache.harmony.jpda.tests.framework.jdwp.EventPacket;
31import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
32import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
33import org.apache.harmony.jpda.tests.framework.jdwp.Packet;
34import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
35
36/**
37 * Basic class for unit tests which use only one debuggee VM.
38 */
39public abstract class JDWPTestCase extends JDWPRawTestCase {
40
41    /**
42     * DebuggeeWrapper instance for launched debuggee VM.
43     */
44    protected JDWPUnitDebuggeeWrapper debuggeeWrapper;
45
46    /**
47     * EventPacket instance with received VM_START event.
48     */
49    protected EventPacket initialEvent = null;
50
51    /**
52     * Overrides inherited method to launch one debuggee VM, establish JDWP
53     * connection, and wait for VM_START event.
54     */
55    protected void internalSetUp() throws Exception {
56        super.internalSetUp();
57
58        // launch debuggee process
59        debuggeeWrapper = createDebuggeeWrapper();
60        beforeConnectionSetUp();
61        setUpDebuggeeWrapperConnection();
62        beforeDebuggeeStart();
63        startDebuggeeWrapper();
64
65        // receive and handle initial event
66        receiveInitialEvent();
67
68        // adjust JDWP types length
69        debuggeeWrapper.vmMirror.adjustTypeLength();
70        logWriter.println("Adjusted VM-dependent type lengths");
71    }
72
73    /**
74     * Creates wrapper for debuggee process.
75     */
76    protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() {
77        if (settings.getDebuggeeLaunchKind().equals("manual")) {
78            return new JDWPManualDebuggeeWrapper(settings, logWriter);
79        } else {
80            return new JDWPUnitDebuggeeWrapper(settings, logWriter);
81        }
82    }
83
84    /**
85     * Set up server side JDWP connection.
86     */
87    protected void setUpDebuggeeWrapperConnection() {
88        debuggeeWrapper.setUpConnection();
89        logWriter.println("Set up server side JDWP connection.");
90    }
91
92    /**
93     * Starts wrapper for debuggee process.
94     */
95    protected void startDebuggeeWrapper() {
96        debuggeeWrapper.start();
97        logWriter.println("Established JDWP connection with debuggee VM");
98    }
99
100    /**
101     * Receives initial VM_INIT event if debuggee is suspended on event.
102     */
103    protected void receiveInitialEvent() {
104        if (settings.isDebuggeeSuspend()) {
105            initialEvent =
106                debuggeeWrapper.vmMirror.receiveCertainEvent(JDWPConstants.EventKind.VM_INIT);
107            logWriter.println("Received inital VM_INIT event");
108        }
109    }
110
111    /**
112     * Overrides inherited method to stop started debuggee VM and close all
113     * connections.
114     */
115    protected void internalTearDown() {
116        if (debuggeeWrapper != null) {
117            debuggeeWrapper.stop();
118            logWriter.println("Closed JDWP connection with debuggee VM");
119        }
120        super.internalTearDown();
121    }
122
123    /**
124     * This method is invoked right before setting up the server side JDWP connection.
125     */
126    protected void beforeConnectionSetUp() {
127      // Empty.
128    }
129
130    /**
131     * This method is invoked right before starting debuggee VM.
132     */
133    protected void beforeDebuggeeStart() {
134      // Empty.
135    }
136
137    /**
138     * Opens JDWP connection with debuggee (doesn't run debuggee and doesn't
139     * establish synchronize connection).
140     */
141    public void openConnection() {
142        debuggeeWrapper.openConnection();
143        logWriter.println("Opened transport connection");
144        debuggeeWrapper.vmMirror.adjustTypeLength();
145        logWriter.println("Adjusted VM-dependent type lengths");
146    }
147
148    /**
149     * Closes JDWP connection with debuggee (doesn't terminate debuggee and
150     * doesn't stop synchronize connection).
151     */
152    public void closeConnection() {
153        if (debuggeeWrapper != null) {
154            debuggeeWrapper.disposeConnection();
155            try {
156                debuggeeWrapper.vmMirror.closeConnection();
157            } catch (Exception e) {
158                throw new TestErrorException(e);
159            }
160            logWriter.println("Closed transport connection");
161        }
162    }
163
164    /**
165     * Helper that returns reference type signature of input object ID.
166     *
167     * @param objectID -
168     *            debuggee object ID
169     * @return object signature of reference type
170     */
171    protected String getObjectSignature(long objectID) {
172        long classID = getObjectReferenceType(objectID);
173        return getClassSignature(classID);
174    }
175
176    /**
177     * Helper that returns reference type ID for input object ID.
178     *
179     * @param objectID -
180     *            debuggee object ID
181     * @return reference type ID
182     */
183    protected long getObjectReferenceType(long objectID) {
184        CommandPacket command = new CommandPacket(
185                JDWPCommands.ObjectReferenceCommandSet.CommandSetID,
186                JDWPCommands.ObjectReferenceCommandSet.ReferenceTypeCommand);
187        command.setNextValueAsReferenceTypeID(objectID);
188        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(command);
189        checkReplyPacket(reply, "ObjectReference::ReferenceType command");
190        // byte refTypeTag =
191        reply.getNextValueAsByte();
192        long objectRefTypeID = reply.getNextValueAsReferenceTypeID();
193        return objectRefTypeID;
194    }
195
196    /**
197     * Helper for getting method ID of corresponding class and method name.
198     *
199     * @param classID -
200     *            class ID
201     * @param methodName -
202     *            method name
203     * @return method ID
204     */
205    protected long getMethodID(long classID, String methodName) {
206        CommandPacket command = new CommandPacket(
207                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
208                JDWPCommands.ReferenceTypeCommandSet.MethodsCommand);
209        command.setNextValueAsClassID(classID);
210        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(command);
211        checkReplyPacket(reply, "ReferenceType::Methods command");
212        int methods = reply.getNextValueAsInt();
213        for (int i = 0; i < methods; i++) {
214            long methodID = reply.getNextValueAsMethodID();
215            String name = reply.getNextValueAsString(); // method name
216            reply.getNextValueAsString(); // method signature
217            reply.getNextValueAsInt(); // method modifiers
218            if (name.equals(methodName)) {
219                return methodID;
220            }
221        }
222        return -1;
223    }
224
225    /**
226     * Helper for getting method ID of corresponding class, method name and signature.
227     *
228     * @param classID -
229     *            class ID
230     * @param methodName -
231     *            method name
232     * @param methodSignature -
233     *            method signature
234     * @return method ID
235     */
236    protected long getMethodID(long classID, String methodName, String methodSignature) {
237        CommandPacket command = new CommandPacket(
238                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
239                JDWPCommands.ReferenceTypeCommandSet.MethodsCommand);
240        command.setNextValueAsClassID(classID);
241        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(command);
242        checkReplyPacket(reply, "ReferenceType::Methods command");
243        int methods = reply.getNextValueAsInt();
244        for (int i = 0; i < methods; i++) {
245            long methodID = reply.getNextValueAsMethodID();
246            String name = reply.getNextValueAsString(); // method name
247            String signature = reply.getNextValueAsString();
248            reply.getNextValueAsInt(); // method modifiers
249            if (name.equals(methodName) && signature.equals(methodSignature)) {
250                return methodID;
251            }
252        }
253        return -1;
254    }
255
256    /**
257     * Issues LineTable command.
258     *
259     * @param classID -
260     *            class ID
261     * @param methodID -
262     *            method ID
263     * @return reply packet
264     */
265    protected ReplyPacket getLineTable(long classID, long methodID) {
266        CommandPacket lineTableCommand = new CommandPacket(
267                JDWPCommands.MethodCommandSet.CommandSetID,
268                JDWPCommands.MethodCommandSet.LineTableCommand);
269        lineTableCommand.setNextValueAsReferenceTypeID(classID);
270        lineTableCommand.setNextValueAsMethodID(methodID);
271        ReplyPacket lineTableReply = debuggeeWrapper.vmMirror
272                .performCommand(lineTableCommand);
273        checkReplyPacket(lineTableReply, "Method::LineTable command");
274        return lineTableReply;
275    }
276
277    /**
278     * Helper for getting method name of corresponding class and method ID.
279     *
280     * @param classID class id
281     * @param methodID method id
282     * @return String
283     */
284    protected String getMethodName(long classID, long methodID) {
285        CommandPacket packet = new CommandPacket(
286                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
287                JDWPCommands.ReferenceTypeCommandSet.MethodsCommand);
288        packet.setNextValueAsClassID(classID);
289        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
290        checkReplyPacket(reply, "ReferenceType::Methods command");
291        int methods = reply.getNextValueAsInt();
292        for (int i = 0; i < methods; i++) {
293            long mid = reply.getNextValueAsMethodID();
294            String name = reply.getNextValueAsString();
295            reply.getNextValueAsString();
296            reply.getNextValueAsInt();
297            if (mid == methodID) {
298                return name;
299            }
300        }
301        return "unknown";
302    }
303
304    /**
305     * Returns jni signature for selected classID
306     *
307     * @param classID
308     * @return jni signature for selected classID
309     */
310    protected String getClassSignature(long classID) {
311        CommandPacket command = new CommandPacket(
312                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
313                JDWPCommands.ReferenceTypeCommandSet.SignatureCommand);
314        command.setNextValueAsReferenceTypeID(classID);
315        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(command);
316        checkReplyPacket(reply, "ReferenceType::Signature command");
317        String signature = reply.getNextValueAsString();
318        return signature;
319    }
320
321    /**
322     * Returns classID for the selected jni signature
323     *
324     * @param signature
325     * @return classID for the selected jni signature
326     */
327    protected long getClassIDBySignature(String signature) {
328        logWriter.println("=> Getting reference type ID for class: "
329                + signature);
330        CommandPacket packet = new CommandPacket(
331                JDWPCommands.VirtualMachineCommandSet.CommandSetID,
332                JDWPCommands.VirtualMachineCommandSet.ClassesBySignatureCommand);
333        packet.setNextValueAsString(signature);
334        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
335        checkReplyPacket(reply, "VirtualMachine::ClassesBySignature command");
336        int classes = reply.getNextValueAsInt();
337        logWriter.println("=> Returned number of classes: " + classes);
338        long classID = 0;
339        for (int i = 0; i < classes; i++) {
340            reply.getNextValueAsByte();
341            classID = reply.getNextValueAsReferenceTypeID();
342            reply.getNextValueAsInt();
343            // we need the only class, even if there were multiply ones
344            break;
345        }
346        assertTrue(
347                "VirtualMachine::ClassesBySignature command returned invalid classID:<"
348                        + classID + "> for signature " + signature, classID > 0);
349        return classID;
350    }
351
352    /**
353     * Returns reference type ID.
354     *
355     * @param signature
356     * @return type ID for the selected jni signature
357     */
358    protected long getReferenceTypeID(String signature) {
359        CommandPacket packet = new CommandPacket(
360                JDWPCommands.VirtualMachineCommandSet.CommandSetID,
361                JDWPCommands.VirtualMachineCommandSet.ClassesBySignatureCommand);
362        packet.setNextValueAsString(signature);
363        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
364        checkReplyPacket(reply, "VirtualMachine::ClassesBySignature command");
365        int classes = reply.getNextValueAsInt();
366        // this class may be loaded only once
367        assertEquals("Invalid number of classes for reference type: "
368                + signature + ",", 1, classes);
369        byte refTypeTag = reply.getNextValueAsByte();
370        long classID = reply.getNextValueAsReferenceTypeID();
371        int status = reply.getNextValueAsInt();
372        logWriter.println("VirtualMachine.ClassesBySignature: classes="
373                + classes + " refTypeTag=" + refTypeTag + " typeID= " + classID
374                + " status=" + status);
375        assertAllDataRead(reply);
376        assertEquals("", JDWPConstants.TypeTag.CLASS, refTypeTag);
377        return classID;
378    }
379
380    /**
381     * Helper function for resuming debuggee.
382     */
383    protected void resumeDebuggee() {
384        logWriter.println("=> Resume debuggee");
385        CommandPacket packet = new CommandPacket(
386                JDWPCommands.VirtualMachineCommandSet.CommandSetID,
387                JDWPCommands.VirtualMachineCommandSet.ResumeCommand);
388        logWriter.println("Sending VirtualMachine::Resume command...");
389        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
390        checkReplyPacket(reply, "VirtualMachine::Resume command");
391        assertAllDataRead(reply);
392    }
393
394    /**
395     * Performs string creation in debuggee.
396     *
397     * @param value -
398     *            content for new string
399     * @return StringID of new created string
400     */
401    protected long createString(String value) {
402        CommandPacket packet = new CommandPacket(
403                JDWPCommands.VirtualMachineCommandSet.CommandSetID,
404                JDWPCommands.VirtualMachineCommandSet.CreateStringCommand);
405        packet.setNextValueAsString(value);
406        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
407        checkReplyPacket(reply, "VirtualMachine::CreateString command");
408        long stringID = reply.getNextValueAsStringID();
409        return stringID;
410    }
411
412    /**
413     * Returns corresponding string from string ID.
414     *
415     * @param stringID -
416     *            string ID
417     * @return string value
418     */
419    protected String getStringValue(long stringID) {
420        CommandPacket packet = new CommandPacket(
421                JDWPCommands.StringReferenceCommandSet.CommandSetID,
422                JDWPCommands.StringReferenceCommandSet.ValueCommand);
423        packet.setNextValueAsObjectID(stringID);
424        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
425        checkReplyPacket(reply, "StringReference::Value command");
426        String returnedTestString = reply.getNextValueAsString();
427        return returnedTestString;
428    }
429
430    /**
431     * Multiple field verification routine.
432     *
433     * @param refTypeID -
434     *            reference type ID
435     * @param checkedFieldNames -
436     *            list of field names to be checked
437     * @return list of field IDs
438     */
439    protected long[] checkFields(long refTypeID, String checkedFieldNames[]) {
440        return checkFields(refTypeID, checkedFieldNames, null, null);
441    }
442
443    /**
444     * Single field verification routine.
445     *
446     * @param refTypeID -
447     *            reference type ID
448     * @param fieldName -
449     *            name of single field
450     * @return filed ID
451     */
452    protected long checkField(long refTypeID, String fieldName) {
453        return checkFields(refTypeID, new String[] { fieldName }, null, null)[0];
454    }
455
456    /**
457     * Multiple field verification routine.
458     *
459     * @param refTypeID -
460     *            reference type ID
461     * @param checkedFieldNames -
462     *            list of field names to be checked
463     * @param expectedSignatures -
464     *            list of expected field signatures
465     * @param expectedModifiers -
466     *            list of expected field modifiers
467     * @return list of field IDs
468     */
469    protected long[] checkFields(long refTypeID, String checkedFieldNames[],
470            String expectedSignatures[], int expectedModifiers[]) {
471
472        boolean checkedFieldFound[] = new boolean[checkedFieldNames.length];
473        long checkedFieldIDs[] = new long[checkedFieldNames.length];
474
475        logWriter
476                .println("=> Send ReferenceType::Fields command and get field ID(s)");
477
478        CommandPacket fieldsCommand = new CommandPacket(
479                JDWPCommands.ReferenceTypeCommandSet.CommandSetID,
480                JDWPCommands.ReferenceTypeCommandSet.FieldsCommand);
481        fieldsCommand.setNextValueAsReferenceTypeID(refTypeID);
482        ReplyPacket fieldsReply = debuggeeWrapper.vmMirror
483                .performCommand(fieldsCommand);
484        fieldsCommand = null;
485        checkReplyPacket(fieldsReply, "ReferenceType::Fields command");
486
487        int returnedFieldsNumber = fieldsReply.getNextValueAsInt();
488        logWriter
489                .println("=> Returned fields number = " + returnedFieldsNumber);
490
491        int checkedFieldsNumber = checkedFieldNames.length;
492        final int fieldSyntheticFlag = 0xf0000000;
493
494        int nameDuplicated = 0;
495        String fieldNameDuplicated = null; // <= collects all duplicated fields
496        int nameMissing = 0;
497        String fieldNameMissing = null; // <= collects all missed fields
498
499        for (int i = 0; i < returnedFieldsNumber; i++) {
500            long returnedFieldID = fieldsReply.getNextValueAsFieldID();
501            String returnedFieldName = fieldsReply.getNextValueAsString();
502            String returnedFieldSignature = fieldsReply.getNextValueAsString();
503            int returnedFieldModifiers = fieldsReply.getNextValueAsInt();
504            logWriter.println("");
505            logWriter.println("=> Field ID: " + returnedFieldID);
506            logWriter.println("=> Field name: " + returnedFieldName);
507            logWriter.println("=> Field signature: " + returnedFieldSignature);
508            logWriter.println("=> Field modifiers: 0x"
509                    + Integer.toHexString(returnedFieldModifiers));
510            if ((returnedFieldModifiers & fieldSyntheticFlag) == fieldSyntheticFlag) {
511                continue; // do not check synthetic fields
512            }
513            for (int k = 0; k < checkedFieldsNumber; k++) {
514                if (!checkedFieldNames[k].equals(returnedFieldName)) {
515                    continue;
516                }
517                if (checkedFieldFound[k]) {
518                    logWriter.println("");
519                    logWriter
520                            .println("## FAILURE: The field is found repeatedly in the list");
521                    logWriter.println("## Field Name: " + returnedFieldName);
522                    logWriter.println("## Field ID: " + returnedFieldID);
523                    logWriter.println("## Field Signature: "
524                            + returnedFieldSignature);
525                    logWriter.println("## Field Modifiers: 0x"
526                            + Integer.toHexString(returnedFieldModifiers));
527                    fieldNameDuplicated = (0 == nameDuplicated ? returnedFieldName
528                            : fieldNameDuplicated + "," + returnedFieldName);
529                    nameDuplicated++;
530                    break;
531                }
532                checkedFieldFound[k] = true;
533                checkedFieldIDs[k] = returnedFieldID;
534                if (null != expectedSignatures) {
535                    assertString(
536                            "Invalid field signature is returned for field:"
537                                    + returnedFieldName + ",",
538                            expectedSignatures[k], returnedFieldSignature);
539                }
540                if (null != expectedModifiers) {
541                    assertEquals(
542                            "Invalid field modifiers are returned for field:"
543                                    + returnedFieldName + ",",
544                            expectedModifiers[k], returnedFieldModifiers);
545                }
546                break;
547            }
548        }
549
550        for (int k = 0; k < checkedFieldsNumber; k++) {
551            if (!checkedFieldFound[k]) {
552                logWriter.println("");
553                logWriter
554                        .println("\n## FAILURE: Expected field is NOT found in the list of retuned fields:");
555                logWriter.println("## Field name = " + checkedFieldNames[k]);
556                fieldNameMissing = 0 == nameMissing ? checkedFieldNames[k]
557                        : fieldNameMissing + "," + checkedFieldNames[k];
558                nameMissing++;
559                // break;
560            }
561        }
562
563        // String thisTestName = this.getClass().getName();
564        // logWriter.println("==> " + thisTestName + " for " + thisCommandName +
565        // ": FAILED");
566
567        if (nameDuplicated > 1) {
568            fail("Duplicated fields are found in the retuned by FieldsCommand list: "
569                    + fieldNameDuplicated);
570        }
571        if (nameDuplicated > 0) {
572            fail("Duplicated field is found in the retuned by FieldsCommand list: "
573                    + fieldNameDuplicated);
574        }
575        if (nameMissing > 1) {
576            fail("Expected fields are NOT found in the retuned by FieldsCommand list: "
577                    + fieldNameMissing);
578        }
579        if (nameMissing > 0) {
580            fail("Expected field is NOT found in the retuned by FieldsCommand list: "
581                    + fieldNameMissing);
582        }
583
584        logWriter.println("");
585        if (1 == checkedFieldsNumber) {
586            logWriter
587                    .println("=> Expected field was found and field ID was got");
588        } else {
589            logWriter
590                    .println("=> Expected fields were found and field IDs were got");
591        }
592
593        assertAllDataRead(fieldsReply);
594        return checkedFieldIDs;
595    }
596
597    /**
598     * Checks thread status and suspend status of a thread
599     *
600     * @param eventThreadID
601     *          the thread ID to check
602     * @param expectedThreadStatus
603     *          the expected thread status
604     * @param expectedSuspendStatus
605     *          the expected suspend status
606     */
607    protected void checkThreadState(long eventThreadID, byte expectedThreadStatus,
608            byte expectedSuspendStatus) {
609        CommandPacket commandPacket = new CommandPacket(
610                JDWPCommands.ThreadReferenceCommandSet.CommandSetID,
611                JDWPCommands.ThreadReferenceCommandSet.StatusCommand);
612        commandPacket.setNextValueAsThreadID(eventThreadID);
613        ReplyPacket replyPacket = debuggeeWrapper.vmMirror.performCommand(commandPacket);
614        debuggeeWrapper.vmMirror.checkReply(replyPacket);
615
616        int threadStatus = replyPacket.getNextValueAsInt();
617        int suspendStatus = replyPacket.getNextValueAsInt();
618        assertAllDataRead(replyPacket);
619
620        assertEquals("Invalid thread status", threadStatus, expectedThreadStatus,
621                JDWPConstants.ThreadStatus.getName(expectedThreadStatus),
622                JDWPConstants.ThreadStatus.getName(threadStatus));
623        assertEquals("Invalid suspend status", suspendStatus, expectedSuspendStatus,
624                JDWPConstants.SuspendStatus.getName(expectedSuspendStatus),
625                JDWPConstants.SuspendStatus.getName(suspendStatus));
626    }
627
628    /**
629     * Helper for checking reply packet error code. Calls junit fail if packet
630     * error code does not equal to expected error code.
631     *
632     * @param reply -
633     *            returned from debuggee packet
634     * @param message -
635     *            additional message
636     * @param errorCodeExpected -
637     *            array of expected error codes
638     */
639    protected void checkReplyPacket(ReplyPacket reply, String message,
640            int errorCodeExpected) {
641        checkReplyPacket(reply, message, new int[] { errorCodeExpected });
642    }
643
644    /**
645     * Helper for checking reply packet error code. Calls junit fail if packet
646     * error code does not equal NONE.
647     *
648     * @param reply -
649     *            returned from debuggee packet
650     * @param message -
651     *            additional message
652     */
653    protected void checkReplyPacket(ReplyPacket reply, String message) {
654        checkReplyPacket(reply, message, JDWPConstants.Error.NONE);
655    }
656
657    /**
658     * Helper for checking reply packet error code. Calls junit fail if packet
659     * error code does not equal to expected error code.
660     *
661     * @param reply -
662     *            returned from debuggee packet
663     * @param message -
664     *            additional message
665     * @param expected -
666     *            array of expected error codes
667     */
668    protected void checkReplyPacket(ReplyPacket reply, String message,
669            int[] expected) {
670        checkReplyPacket(reply, message, expected, true /* failSign */);
671    }
672
673    /**
674     * Helper for checking reply packet error code. If reply packet does not
675     * have error - returns true. Otherwise does not call junit fail - simply
676     * prints error message and returns false. if packet error code does not
677     * equal NONE.
678     *
679     * @param reply -
680     *            returned from debuggee packet
681     * @param message -
682     *            additional message
683     * @return true if error is not found, or false otherwise
684     */
685    protected boolean checkReplyPacketWithoutFail(ReplyPacket reply,
686            String message) {
687        return checkReplyPacket(reply, message,
688                new int[] { JDWPConstants.Error.NONE }, false /* failSign */);
689    }
690
691    /**
692     * Helper for checking reply packet error code. If reply packet does not
693     * have unexpected error - returns true. If reply packet has got unexpected
694     * error: If failSign param = true - calls junit fail. Otherwise prints
695     * message about error and returns false.
696     *
697     * @param reply -
698     *            returned from debuggee packet
699     * @param message -
700     *            additional message
701     * @param expected -
702     *            array of expected error codes
703     * @param failSign -
704     *            defines to call junit fail or not
705     * @return true if unexpected errors are not found, or false otherwise
706     */
707    protected boolean checkReplyPacket(ReplyPacket reply, String message,
708            int[] expected, boolean failSign) {
709        // check reply code against expected
710        int errorCode = reply.getErrorCode();
711        for (int i = 0; i < expected.length; i++) {
712            if (reply.getErrorCode() == expected[i]) {
713                return true; // OK
714            }
715        }
716
717        // replay code validation failed
718        // start error message composition
719        if (null == message) {
720            message = "";
721        } else {
722            message = message + ", ";
723        }
724
725        // format error message
726        if (expected.length == 1 && JDWPConstants.Error.NONE == expected[0]) {
727            message = message + "Error Code:<" + errorCode + "("
728                    + JDWPConstants.Error.getName(errorCode) + ")>";
729        } else {
730            message = message + "Unexpected error code:<" + errorCode + "("
731                    + JDWPConstants.Error.getName(errorCode) + ")>"
732                    + ", Expected error code"
733                    + (expected.length == 1 ? ":" : "s:");
734            for (int i = 0; i < expected.length; i++) {
735                message = message + (i > 0 ? ",<" : "<") + expected[i] + "("
736                        + JDWPConstants.Error.getName(expected[i]) + ")>";
737            }
738        }
739
740        if (failSign) {
741            printErrorAndFail(message);
742        }
743        logWriter.printError(message);
744        return false;
745    }
746
747    /**
748     * Helper for comparison numbers and printing string equivalents.
749     *
750     * @param message -
751     *            user message
752     * @param expected -
753     *            expected value
754     * @param actual -
755     *            actual value
756     * @param strExpected -
757     *            string equivalent of expected value
758     * @param strActual -
759     *            string equivalent of actual value
760     */
761    protected void assertEquals(String message, long expected, long actual,
762            String strExpected, String strActual) {
763        if (expected == actual) {
764            return; // OK
765        }
766
767        if (null == message) {
768            message = "";
769        }
770
771        if (null == strExpected) {
772            strExpected = expected + "";
773        } else {
774            strExpected = expected + "(" + strExpected + ")";
775        }
776
777        if (null == strActual) {
778            strActual = actual + "";
779        } else {
780            strActual = actual + "(" + strActual + ")";
781        }
782
783        printErrorAndFail(message + " expected:<" + strExpected + "> but was:<"
784                + strActual + ">");
785    }
786
787    /**
788     * Asserts that two strings are equal.
789     *
790     * @param message -
791     *            user message
792     * @param expected -
793     *            expected string
794     * @param actual -
795     *            actual string
796     */
797    protected void assertString(String message, String expected, String actual) {
798        if (null == expected) {
799            expected = "";
800        }
801        if (null == actual) {
802            actual = "";
803        }
804        if (expected.equals(actual)) {
805            return; // OK
806        }
807        printErrorAndFail(message + " expected:<" + expected + "> but was:<"
808                + actual + ">");
809    }
810
811    /**
812     * Helper for checking reply packet data has been read.
813     *
814     * @param reply -
815     *            reply packet from debuggee
816     */
817    protected void assertAllDataRead(Packet reply) {
818        if (reply.isAllDataRead()) {
819            return; // OK
820        }
821        printErrorAndFail("Not all data has been read");
822    }
823
824    /**
825     * Asserts that two JDWP event kinds are equal.
826     *
827     * @param message
828     *          user message
829     * @param expected
830     *          expected event kind
831     * @param actual
832     *          actual event kind
833     */
834    protected void assertEventKindEquals(String message, byte expected, byte actual) {
835        if (expected != actual) {
836            StringBuilder builder = new StringBuilder(message);
837            builder.append(": expected ");
838            builder.append(expected);
839            builder.append(" (");
840            builder.append(JDWPConstants.EventKind.getName(expected));
841            builder.append(") but was ");
842            builder.append(actual);
843            builder.append(" (");
844            builder.append(JDWPConstants.EventKind.getName(actual));
845            builder.append(")");
846            printErrorAndFail(builder.toString());
847        }
848    }
849
850    /**
851     * Asserts that two JDWP tags are equal.
852     *
853     * @param message
854     *          user message
855     * @param expected
856     *          expected tag
857     * @param actual
858     *          actual tag
859     */
860    protected void assertTagEquals(String message, byte expected, byte actual) {
861        if (expected != actual) {
862            StringBuilder builder = new StringBuilder(message);
863            builder.append(": expected ");
864            builder.append(expected);
865            builder.append(" (");
866            builder.append(JDWPConstants.Tag.getName(expected));
867            builder.append(") but was ");
868            builder.append(actual);
869            builder.append(" (");
870            builder.append(JDWPConstants.Tag.getName(actual));
871            builder.append(")");
872            printErrorAndFail(builder.toString());
873        }
874    }
875
876    /**
877     * Prints error message in log writer and in junit fail.
878     *
879     * @param message -
880     *            error message
881     */
882    protected void printErrorAndFail(String message) {
883        logWriter.printError(message);
884        fail(message);
885    }
886
887    /**
888     * Helper for setting static int field in class with new value.
889     *
890     * @param classSignature -
891     *            String defining signature of class
892     * @param fieldName -
893     *            String defining field name in specified class
894     * @param newValue -
895     *            int value to set for specified field
896     * @return true, if setting is successfully, or false otherwise
897     */
898    protected boolean setStaticIntField(String classSignature,
899            String fieldName, int newValue) {
900
901        long classID = debuggeeWrapper.vmMirror.getClassID(classSignature);
902        if (classID == -1) {
903            logWriter
904                    .println("## setStaticIntField(): Can NOT get classID for class signature = '"
905                            + classSignature + "'");
906            return false;
907        }
908
909        long fieldID = debuggeeWrapper.vmMirror.getFieldID(classID, fieldName);
910        if (fieldID == -1) {
911            logWriter
912                    .println("## setStaticIntField(): Can NOT get fieldID for field = '"
913                            + fieldName + "'");
914            return false;
915        }
916
917        CommandPacket packet = new CommandPacket(
918                JDWPCommands.ClassTypeCommandSet.CommandSetID,
919                JDWPCommands.ClassTypeCommandSet.SetValuesCommand);
920        packet.setNextValueAsReferenceTypeID(classID);
921        packet.setNextValueAsInt(1);
922        packet.setNextValueAsFieldID(fieldID);
923        packet.setNextValueAsInt(newValue);
924
925        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
926        int errorCode = reply.getErrorCode();
927        if (errorCode != JDWPConstants.Error.NONE) {
928            logWriter
929                    .println("## setStaticIntField(): Can NOT set value for field = '"
930                            + fieldName
931                            + "' in class = '"
932                            + classSignature
933                            + "'; ClassType.SetValues command reurns error = "
934                            + errorCode);
935            return false;
936        }
937        return true;
938    }
939
940    /**
941     * Removes breakpoint of the given event kind corresponding to the given
942     * request id.
943     *
944     * @param eventKind
945     *            request event kind
946     * @param requestID
947     *            request id
948     * @param verbose
949     *            print or don't extra log info
950     */
951    protected void clearEvent(byte eventKind, int requestID, boolean verbose) {
952        CommandPacket packet = new CommandPacket(
953                JDWPCommands.EventRequestCommandSet.CommandSetID,
954                JDWPCommands.EventRequestCommandSet.ClearCommand);
955        packet.setNextValueAsByte(eventKind);
956        packet.setNextValueAsInt(requestID);
957        if (verbose) {
958            logWriter.println("Clearing event: "
959                    + JDWPConstants.EventKind.getName(eventKind) + ", id: "
960                    + requestID);
961        }
962        ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet);
963        checkReplyPacket(reply, "EventRequest::Clear command");
964        assertAllDataRead(reply);
965    }
966}
967