/* * 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. * *

There are some points that should be fulfilled in a subclass to work * correctly in the multi-method framework: * * 1. each subclass should 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 _()', 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 */ @SuppressWarnings("unused") 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 final ArrayList methCalled = new ArrayList(10); /** * 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 is called 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; 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() throws Exception { } /** * 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 */ private void setStatus(String methName, Status methStatus) { tRes.tested(methName, methStatus); } /** * sets the substates */ private 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 method 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 method test has been already called. */ private boolean isCalled(String method) { return methCalled.contains(method); } /** * Calling of the method indicates that the method 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.isCompleted() || mtStatus.isFailed())) { log.println("! Required method " + method + " failed"); throw new StatusException(mtStatus); } } /** * Checks if the method 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 method test. */ private 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 method of the interface. * * @return the testing method, if found, null otherwise */ private 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); } protected final void waitForEventIdle() { util.utils.waitForEventIdle(tParam.getMSF()); } }