2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 import java
.io
.PrintWriter
;
21 import java
.lang
.reflect
.InvocationTargetException
;
22 import java
.lang
.reflect
.Method
;
23 import java
.util
.ArrayList
;
25 import share
.DescEntry
;
26 import stats
.Summarizer
;
28 import com
.sun
.star
.uno
.UnoRuntime
;
31 * The class supports method based interface tests development.
33 * <p>There are some points that should be fulfilled in a subclass to work
34 * correctly in the multi-method framework:
36 * 1. each subclass should define a public field named oObj of type tested
37 * by the subclass, e.g. 'public XText oObj;'. That field will be initialized
38 * by the MultiMethodTest code with the instance of the interface to test.
39 * In a case of service testing the field type should be XPropertySet.
41 * 2. for the test of each method of the tested interface(or a property in the
42 * case of service testing) should be method with the following signature
43 * provided: 'public void _<method name>()', e.g. 'public void _getText()'.
44 * The methods will be called by MultiMethodText code using reflection API
45 * for each method in the interface description.
47 * 3. to set status for a call 'tRes.tested(String method,
48 * boolean result)' should be used. For example 'tRes.tested("getText()",
49 * true)'. Also 'tRes.assert(String assertion, boolean result)' call can
50 * be used. Note, that one can call the methods not necessarily from the
51 * test for the tested method, but from other method tests too (in the
52 * MultiMethodTest subclass). See also TestResult and MultiMethodTest.tRes
55 * 4. the before() and after() methods can be overridden to perform some
56 * actions, accordingly, before and after calling the test methods.
58 * 5. besides tRes, there are some fields initialized in the MultiMethodTest,
59 * that can be used for implementing tests:
61 * - tEnv contains the environment tested
62 * - tParam contains parameters of the test
63 * - log a writer to log information about the test
67 @SuppressWarnings("unused")
68 public class MultiMethodTest
72 * Contains the TestEnvironment being tested, to allow for tests to access
75 protected TestEnvironment tEnv
;
77 * Contains the TestParameters for the tests, to allow for tests to access
80 protected TestParameters tParam
;
82 * Contains the Description for the test
85 protected DescEntry entry
;
87 * Contains a writer to log an information about the interface testing, to
88 * allows for tests to access it.
90 protected PrintWriter log
;
92 * Contains the TestResult instance for the interface test to collect
93 * information about methods test.
95 protected TestResult tRes
;
97 * Contains names of the methods have been already called
99 private final ArrayList
<String
> methCalled
= new ArrayList
<String
>(10);
102 * Disposes the current test environment, which was corrupted by the test.
104 * @see #disposeEnvironment(TestEnvironment)
106 public void disposeEnvironment()
109 TestCase tCase
= tEnv
.getTestCase();
110 tCase
.disposeTestEnvironment(tEnv
, tParam
);
114 * Runs the interface test: its method tests. First, it initializes some
115 * of MultiMethodTest fields, like tRes, log, tEnv, etc. Then, it queries
116 * the tested interface and initializes 'oObj' field (defined in a
117 * subclass). Before calling method tests, before() method is called to allow
118 * initialization of s stuff before testing. Then, the method tests are
119 * called. After them, after() method is called, to allow cleaning up the
120 * stuff initialized in before() and test methods.
122 * @param entry the interface test state
123 * @param tEnv the environment to test
124 * @param tParam the parameters of the test
129 public TestResult
run(DescEntry entry
, TestEnvironment tEnv
, TestParameters tParam
)
132 log
= (PrintWriter
) entry
.Logger
;
135 this.tParam
= tParam
;
138 this.tRes
= new TestResult();
139 Class
<?
> testedClass
;
141 System
.out
.print("checking: [" + entry
.longName
+ "]");
143 // defining a name of the class corresponding to the tested interface
145 String testedClassName
;
147 testedClassName
= getTestedClassName();
149 if (entry
.EntryType
.equals("service"))
151 testedClassName
= "com.sun.star.beans.XPropertySet";
156 testedClass
= Class
.forName(testedClassName
);
158 catch (ClassNotFoundException cnfE
)
160 System
.out
.println();
161 cnfE
.printStackTrace(log
);
162 log
.println("could not find a class : " + getTestedClassName());
165 System
.out
.println(" is iface: [" + testedClassName
+ "] testcode: [" + entry
.entryName
+ "]");
167 Object oObj
= UnoRuntime
.queryInterface(testedClass
, tEnv
.getTestObject());
171 if (entry
.isOptional
)
173 Summarizer
.summarizeDown(entry
, "Not supported but optional.OK");
177 Summarizer
.summarizeDown(entry
, "queryInterface returned null.FAILED");
178 entry
.ErrorMsg
= "queryInterface returned null";
179 entry
.hasErrorMsg
= true;
185 //setting the field oObj
188 setField("oObj", oObj
);
193 setSubStates(e
.toString());
197 // to perform some stuff before all method tests
205 setSubStates(e
.toString());
209 // executing methods tests
210 for (int i
= 0; i
< entry
.SubEntryCount
; i
++)
212 DescEntry aSubEntry
= entry
.SubEntries
[i
];
215 final String sEntryName
= aSubEntry
.entryName
;
216 executeMethod(sEntryName
);
220 log
.println("Exception while checking: " + aSubEntry
.entryName
+ " : " + e
.getMessage());
224 // to perform some stuff after all method tests
237 * Is called before calling method tests, but after initialization.
238 * Subclasses may override to perform actions before method tests.
240 protected void before() throws Exception
245 * Is called after calling method tests. Subclasses may override
246 * to perform actions after method tests.
248 protected void after()
253 * @return the name of the interface or the service tested.
255 protected String
getTestedClassName()
257 String clsName
= this.getClass().getName();
259 int firstDot
= clsName
.indexOf('.');
260 int lastDot
= clsName
.lastIndexOf('.');
262 String append
= "com.sun.star.";
264 if (entry
.longName
.indexOf("::drafts::com::") > -1)
266 append
= "drafts.com.sun.star.";
269 return append
+ clsName
.substring(firstDot
+ 1, lastDot
+ 1) + clsName
.substring(lastDot
+ 2);
273 * Sets a method status.
275 * @param methName the method name to set status
276 * @param methStatus the status to set to the method
278 private void setStatus(String methName
, Status methStatus
)
280 tRes
.tested(methName
, methStatus
);
286 private void setSubStates(String msg
)
288 for (int k
= 0; k
< entry
.SubEntryCount
; k
++)
290 entry
.SubEntries
[k
].hasErrorMsg
= true;
291 entry
.SubEntries
[k
].ErrorMsg
= msg
;
292 if (entry
.SubEntries
[k
].State
.equals("UNKNOWN"))
294 entry
.SubEntries
[k
].State
= msg
;
301 * Checks if the <code>method</code> is optional in the service.
303 protected boolean isOptional(String _method
)
305 for (int k
= 0; k
< entry
.SubEntryCount
; k
++)
307 final String sName
= entry
.SubEntries
[k
].entryName
;
308 if (sName
.equals(_method
))
310 final boolean bIsOptional
= entry
.SubEntries
[k
].isOptional
;
318 * Checks if the <code>method</code> test has been already called.
320 private boolean isCalled(String method
)
322 return methCalled
.contains(method
);
326 * Calling of the method indicates that the <code>method</code> test should
327 * be called. The method checks this and if it is not called, calls it.
328 * If the method is failed or skipped, it throws StatusException.
330 protected void requiredMethod(String method
)
332 log
.println("starting required method: " + method
);
333 executeMethod(method
);
334 Status mtStatus
= tRes
.getStatusFor(method
);
336 if (mtStatus
!= null && (!mtStatus
.isCompleted() || mtStatus
.isFailed()))
338 log
.println("! Required method " + method
+ " failed");
339 throw new StatusException(mtStatus
);
344 * Checks if the <code>method</code> was called, and if not, call it.
345 * On contrary to requiredMethod(), he method doesn't check its status.
347 protected void executeMethod(String method
)
349 if (!isCalled(method
))
351 log
.println("Execute: " + method
);
353 log
.println(method
+ ": " + tRes
.getStatusFor(method
));
359 * Just calls the <code>method</code> test.
361 private void callMethod(String method
)
363 methCalled
.add(method
);
364 invokeTestMethod(getMethodFor(method
), method
);
368 * Invokes a test method of the subclass using reflection API. Handles
369 * the method results and sets its status.
371 * @param meth the subclass' method to invoke
372 * @param methName the name of the method
374 protected void invokeTestMethod(Method meth
, String methName
)
378 setStatus(methName
, Status
.skipped(false));
386 meth
.invoke(this, new Object
[0]);
389 catch (InvocationTargetException itE
)
391 Throwable t
= itE
.getTargetException();
393 if (t
instanceof StatusException
)
395 stat
= ((StatusException
) t
).getStatus();
399 t
.printStackTrace(log
);
400 stat
= Status
.exception(t
);
403 catch (IllegalAccessException iaE
)
405 iaE
.printStackTrace(log
);
406 stat
= Status
.exception(iaE
);
408 catch (IllegalArgumentException iaE
)
410 iaE
.printStackTrace(log
);
411 stat
= Status
.exception(iaE
);
413 catch (ClassCastException ccE
)
415 ccE
.printStackTrace(log
);
416 stat
= Status
.exception(ccE
);
419 setStatus(methName
, stat
);
424 * Finds a testing method for the <code>method</code> of the interface.
426 * @return the testing method, if found, <tt>null</tt> otherwise
428 private Method
getMethodFor(String method
)
430 String mName
= "_" + method
;
432 if (mName
.endsWith("()"))
434 mName
= mName
.substring(0, mName
.length() - 2);
437 final Class
<?
>[] paramTypes
= new Class
[0];
441 return this.getClass().getDeclaredMethod(mName
, paramTypes
);
443 catch (NoSuchMethodException nsmE
)
450 * @return the name of the interface tested
452 public String
getInterfaceName()
454 String clName
= this.getClass().getName();
455 return clName
.substring(clName
.lastIndexOf('.') + 1);
458 private void setField(String fieldName
, Object value
)
459 throws NoSuchFieldException
, IllegalAccessException
461 this.getClass().getField(fieldName
).set(this, value
);
464 protected final void waitForEventIdle() {
465 util
.utils
.waitForEventIdle(tParam
.getMSF());