| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| package lib; |
| |
| import java.io.PrintWriter; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.util.Vector; |
| |
| import com.sun.star.uno.UnoRuntime; |
| import com.sun.star.uno.XInterface; |
| |
| import share.DescEntry; |
| import lib.TestParameters; |
| import stats.Summarizer; |
| |
| /** |
| * 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 neccesarily 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 overriden 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 alreadycalled |
| */ |
| private Vector methCalled = new Vector(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 (! entry.entryName.equals("ifc.qadevooo._SelfTest")) |
| { |
| String ifcName = getInterfaceName(); |
| // System.out.println("checking : " + ifcName); |
| 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 + "]"); |
| |
| // quering the tested interface from the tested object |
| XInterface tCase = tEnv.getTestObject(); |
| 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 |
| setField("oObj", oObj); |
| } |
| |
| // to perform some stuff before all method tests |
| try |
| { |
| before(); |
| } |
| catch (Exception e) |
| { |
| 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); |
| } |
| |
| /** |
| * Initializes <code>fieldName</code> of the subclass with |
| * <code>value</code>. |
| * |
| * @return Status describing the result of the operation. |
| */ |
| protected Status setField(String fieldName, Object value) |
| { |
| Field objField; |
| |
| try |
| { |
| objField = this.getClass().getField(fieldName); |
| } |
| catch (NoSuchFieldException nsfE) |
| { |
| return Status.exception(nsfE); |
| } |
| |
| try |
| { |
| objField.set(this, value); |
| return Status.passed(true); |
| } |
| catch (IllegalArgumentException iaE) |
| { |
| return Status.exception(iaE); |
| } |
| catch (IllegalAccessException iaE) |
| { |
| return Status.exception(iaE); |
| } |
| } |
| } |