summaryrefslogtreecommitdiff
path: root/qadevOOo/runner/lib/MultiMethodTest.java
blob: 0493b4bbf37e1ea9a0c770215aa8a5f31de7a3b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
package lib;

import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

import share.DescEntry;
import stats.Summarizer;

import com.sun.star.uno.UnoRuntime;

/**
 * The class supports method based interface tests development.
 *
 * <p>There are some points that should be fulfilled in a subclass to work
 * correctly in the multi-method framework:
 *
 *   1. each subclass schould define a public field named oObj of type tested
 *   by the subclass, e.g. 'public XText oObj;'. That field will be initialized
 *   by the MultiMethodTest code with the instance of the interface to test.
 *   In a case of service testing the field type should be XPropertySet.
 *
 *   2. for the test of each method of the tested interface(or a property in the
 *   case of service testing) should be method with the following signature
 *   provided: 'public void _<method name>()', e.g. 'public void _getText()'.
 *   The methods will be called by MultiMethodText code using reflection API
 *   for each method in the interface description.
 *
 *   3. to set status for a call 'tRes.tested(String method,
 *   boolean result)' should be used. For example 'tRes.tested("getText()",
 *   true)'. Also 'tRes.assert(String assertion, boolean result)' call can
 *   be used. Note, that one can call the methods not necessarily from the
 *   test for the tested method, but from other method tests too (in the
 *   MultiMethodTest subclass). See also TestResult and MultiMethodTest.tRes
 *   documentation.
 *
 *   4. the before() and after() methods can be overridden to perform some
 *   actions, accordingly, before and after calling the test methods.
 *
 *   5. besides tRes, there are some fields initialized in the MultiMethodTest,
 *   that can be used for implementing tests:
 *
 *     - tEnv contains the environment tested
 *     - tParam contains parameters of the test
 *     - log a writer to log information about the test
 *
 * @see TestResult
 */
public class MultiMethodTest
{

    /**
     * Contains the TestEnvironment being tested, to allow for tests to access
     * it.
     */
    protected TestEnvironment tEnv;
    /**
     * Contains the TestParameters for the tests, to allow for tests to access
     * it.
     */
    protected TestParameters tParam;
    /**
     * Contains the Description for the test
     * it.
     */
    protected DescEntry entry;
    /**
     * Contains a writer to log an information about the interface testing, to
     * allows for tests to access it.
     */
    protected PrintWriter log;
    /**
     * Contains the TestResult instance for the interface test to collect
     * information about methods test.
     */
    protected TestResult tRes;
    /**
     * Contains names of the methods have been already called
     */
    private ArrayList<String> methCalled = new ArrayList<String>(10);

    /**
     * Disposes the test environment, which was corrupted by the test.
     *
     * @param tEnv the environment to dispose
     */
    public void disposeEnvironment(TestEnvironment tEnv)
    {
        disposeEnvironment();
    }

    /**
     * Disposes the current test environment, which was corrupted by the test.
     *
     * @see #disposeEnvironment(TestEnvironment)
     */
    public void disposeEnvironment()
    {
        tEnv.dispose();
        TestCase tCase = tEnv.getTestCase();
        tCase.disposeTestEnvironment(tEnv, tParam);
    }

    /**
     * Runs the interface test: its method tests. First, it initializes some
     * of MultiMethodTest fields, like tRes, log, tEnv, etc. Then, it queries
     * the tested interface and initializes 'oObj' field (defined in a
     * subclass). Before calling method tests, before() method calles to allow
     * initialization of s stuff before testing. Then, the method tests are
     * called. After them, after() method is called, to allow cleaning up the
     * stuff initialized in before() and test methods.
     *
     * @param entry the interface test state
     * @param tEnv the environment to test
     * @param tParam the parameters of the test
     *
     * @see #before
     * @see #after
     */
    public TestResult run(DescEntry entry, TestEnvironment tEnv, TestParameters tParam)
    {

        log = (PrintWriter) entry.Logger;

        this.tEnv = tEnv;
        this.tParam = tParam;
        // this.log = log;
        this.entry = entry;
        this.tRes = new TestResult();
        Class<?> testedClass;

        // Some fake code for a self test.
        // For normal test we must not be a "ifc.qadevooo._SelfTest"
        if (! ("ifc.qadevooo._SelfTest").equals(entry.entryName))
        {
            getInterfaceName();
            System.out.print("checking: [" + entry.longName + "]");

            // defining a name of the class corresponding to the tested interface
            // or service
            String testedClassName;

            testedClassName = getTestedClassName();

            if (entry.EntryType.equals("service"))
            {
                testedClassName = "com.sun.star.beans.XPropertySet";
            }

            try
            {
                testedClass = Class.forName(testedClassName);
            }
            catch (ClassNotFoundException cnfE)
            {
                System.out.println();
                cnfE.printStackTrace(log);
                log.println("could not find a class : " + getTestedClassName());
                return null;
            }
            System.out.println(" is iface: [" + testedClassName + "] testcode: [" + entry.entryName + "]");

            Object oObj = UnoRuntime.queryInterface(testedClass, tEnv.getTestObject());

            if (oObj == null)
            {
                if (entry.isOptional)
                {
                    Summarizer.summarizeDown(entry, "Not supported but optional.OK");
                }
                else
                {
                    Summarizer.summarizeDown(entry, "queryInterface returned null.FAILED");
                    entry.ErrorMsg = "queryInterface returned null";
                    entry.hasErrorMsg = true;
                }

                return null;
            }

            //setting the field oObj
            try
            {
                setField("oObj", oObj);
            }
            catch (Exception e)
            {
                e.printStackTrace();
                setSubStates(e.toString());
                return tRes;
            }
        }

        // to perform some stuff before all method tests
        try
        {
            before();
        }
        catch (Exception e)
        {
            e.printStackTrace();
            setSubStates(e.toString());
            return tRes;
        }

        // executing methods tests
        for (int i = 0; i < entry.SubEntryCount; i++)
        {
            DescEntry aSubEntry = entry.SubEntries[i];
            try
            {
                final String sEntryName = aSubEntry.entryName;
                executeMethod(sEntryName);
            }
            catch (Exception e)
            {
                log.println("Exception while checking: " + aSubEntry.entryName + " : " + e.getMessage());
            }
        }

        // to perform some stuff after all method tests
        try
        {
            after();
        }
        catch (Exception e)
        {
        }

        return tRes;
    }

    /**
     * Is called before calling method tests, but after initialization.
     * Subclasses may override to perform actions before method tests.
     */
    protected void before()
    {
    }

    /**
     * Is called after calling method tests. Subclasses may override
     * to perform actions after method tests.
     */
    protected void after()
    {
    }

    /**
     * @return the name of the interface or the service tested.
     */
    protected String getTestedClassName()
    {
        String clsName = this.getClass().getName();

        int firstDot = clsName.indexOf('.');
        int lastDot = clsName.lastIndexOf('.');

        String append = "com.sun.star.";

        if (entry.longName.indexOf("::drafts::com::") > -1)
        {
            append = "drafts.com.sun.star.";
        }

        return append + clsName.substring(firstDot + 1, lastDot + 1) + clsName.substring(lastDot + 2);
    }

    /**
     * Sets a method status.
     *
     * @param methName the method name to set status
     * @param methStatus the status to set to the method
     */
    protected void setStatus(String methName, Status methStatus)
    {
        tRes.tested(methName, methStatus);
    }

    /**
     * sets the substates
     */
    protected void setSubStates(String msg)
    {
        for (int k = 0; k < entry.SubEntryCount; k++)
        {
            entry.SubEntries[k].hasErrorMsg = true;
            entry.SubEntries[k].ErrorMsg = msg;
            if (entry.SubEntries[k].State.equals("UNKNOWN"))
            {
                entry.SubEntries[k].State = msg;
            }
        }

    }

    /**
     * Checks if the <code>method</code> is optional in the service.
     */
    protected boolean isOptional(String _method)
    {
        for (int k = 0; k < entry.SubEntryCount; k++)
        {
            final String sName = entry.SubEntries[k].entryName;
            if (sName.equals(_method))
            {
                final boolean bIsOptional = entry.SubEntries[k].isOptional;
                return bIsOptional;
            }
        }
        return false;
    }

    /**
     * Checks if the <code>method</code> test has been already called.
     */
    protected boolean isCalled(String method)
    {
        return methCalled.contains(method);
    }

    /**
     * Calling of the method indicates that the <code>method</code> test should
     * be called. The method checks this and if it is not called, calls it.
     * If the method is failed or skipped, it throws StatusException.
     */
    protected void requiredMethod(String method)
    {
        log.println("starting required method: " + method);
        executeMethod(method);
        Status mtStatus = tRes.getStatusFor(method);

        if (mtStatus != null && (!mtStatus.isPassed() || mtStatus.isFailed()))
        {
            log.println("! Required method " + method + " failed");
            throw new StatusException(mtStatus);
        }
    }

    /**
     * Checks if the <code>method</code> was called, and if not, call it.
     * On contrary to requiredMethod(), he method doesn't check its status.
     */
    protected void executeMethod(String method)
    {
        if (!isCalled(method))
        {
            log.println("Execute: " + method);
            callMethod(method);
            log.println(method + ": " + tRes.getStatusFor(method));
            log.println();
        }
    }

    /**
     * Just calls the <code>method</code> test.
     */
    protected void callMethod(String method)
    {
        methCalled.add(method);
        invokeTestMethod(getMethodFor(method), method);
    }

    /**
     * Invokes a test method of the subclass using reflection API. Handles
     * the method results and sets its status.
     *
     * @param meth the subclass' method to invoke
     * @param methName the name of the method
     */
    protected void invokeTestMethod(Method meth, String methName)
    {
        if (meth == null)
        {
            setStatus(methName, Status.skipped(false));
        }
        else
        {
            Status stat;

            try
            {
                meth.invoke(this, new Object[0]);
                return;
            }
            catch (InvocationTargetException itE)
            {
                Throwable t = itE.getTargetException();

                if (t instanceof StatusException)
                {
                    stat = ((StatusException) t).getStatus();
                }
                else
                {
                    t.printStackTrace(log);
                    stat = Status.exception(t);
                }
            }
            catch (IllegalAccessException iaE)
            {
                iaE.printStackTrace(log);
                stat = Status.exception(iaE);
            }
            catch (IllegalArgumentException iaE)
            {
                iaE.printStackTrace(log);
                stat = Status.exception(iaE);
            }
            catch (ClassCastException ccE)
            {
                ccE.printStackTrace(log);
                stat = Status.exception(ccE);
            }

            setStatus(methName, stat);
        }
    }

    /**
     * Finds a testing method for the <code>method</code> of the interface.
     *
     * @return the testing method, if found, <tt>null</tt> otherwise
     */
    protected Method getMethodFor(String method)
    {
        String mName = "_" + method;

        if (mName.endsWith("()"))
        {
            mName = mName.substring(0, mName.length() - 2);
        }

        final Class<?>[] paramTypes = new Class[0];

        try
        {
            return this.getClass().getDeclaredMethod(mName, paramTypes);
        }
        catch (NoSuchMethodException nsmE)
        {
            return null;
        }
    }

    /**
     * @return the name of the interface tested
     */
    public String getInterfaceName()
    {
        String clName = this.getClass().getName();
        return clName.substring(clName.lastIndexOf('.') + 1);
    }

    private void setField(String fieldName, Object value)
        throws NoSuchFieldException, IllegalAccessException
    {
        this.getClass().getField(fieldName).set(this, value);
    }
}