/************************************************************** * * 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 complex.memCheck; import com.sun.star.beans.PropertyValue; import com.sun.star.frame.XStorable; import com.sun.star.lang.XComponent; import com.sun.star.lang.XMultiServiceFactory; import com.sun.star.uno.UnoRuntime; import com.sun.star.util.XCloseable; // import complexlib.ComplexTestCase; import helper.ProcessHandler; import java.io.File; // import java.io.FilePermission; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.PrintWriter; import java.util.Enumeration; import java.util.StringTokenizer; import java.util.Vector; import lib.*; import util.DesktopTools; // import util.WriterTools; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.openoffice.test.OfficeConnection; import static org.junit.Assert.*; /** * Documents are opened and exported with StarOffice. The memory usage of * StarOffice is monitored and if the usage exceeds the allowed kilobytes, * the test is failed. Used for monitoring the StarOffice process is the * command line tool 'pmap', available on Solaris or Linux. This test will not * run on Windows.
Test procedure: every given document type is searched in * the source directory * Needed parameters: * */ class TempDir { private String m_sTempDir; public TempDir(String _sTempDir) { m_sTempDir = _sTempDir; } public String getOfficeTempDir() { return m_sTempDir; } public String getTempDir() { final String sTempDir = FileHelper.getJavaCompatibleFilename(m_sTempDir); return sTempDir; } } public class CheckMemoryUsage /* extends ComplexTestCase */ { private final String sWriterDoc = "sxw,writer_pdf_Export"; private final String sCalcDoc = "sxc,calc_pdf_Export"; private final String sImpressDoc = "sxi,impress_pdf_Export"; // private String sProcessIdCommand = null; TempDir m_aTempDir; // private String sFS = null; // private String sMemoryMap1 = null; // private String sMemoryMap2 = null; // private String sDocumentPath = ""; private String[][] sDocTypeExportFilter; private String[][] sDocuments; private int iAllowMemoryIncrease = 10; private int iExportDocCount = 25; /** * The test parameters */ private static TestParameters param = null; /** * Get all test methods * @return The test methods. // */ // public String[] getTestMethodNames() { // return new String[] {"loadAndSaveDocuments"}; // } /** * Collect all documnets to load and all filters used for export. */ @Before public void before() { final XMultiServiceFactory xMsf = getMSF(); // some Tests need the qadevOOo TestParameters, it is like a Hashmap for Properties. param = new TestParameters(); param.put("ServiceFactory", xMsf); // some qadevOOo functions need the ServiceFactory // test does definitely not run on Windows. if (param.get("OperatingSystem").equals("wntmsci")) { System.out.println("Test can only reasonably be executed with a tool that " + "displays the memory usage of StarOffice."); System.out.println("Test does not run on Windows, only on Solaris or Linux."); // in an automatic environment it is better to say, there is no error here. // it is a limitation, but no error. System.exit(0); } // how many times is every document exported. int count = param.getInt("ExportDocCount"); if (count != 0) { iExportDocCount = count; } // get the temp dir for creating the command scripts. // sTempDir = System.getProperty("java.io.tmpdir"); m_aTempDir = new TempDir(util.utils.getOfficeTemp/*Dir*/(xMsf)); // get the file extension, export filter connection Enumeration keys = param.keys(); Vector v = new Vector(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); if (key.startsWith("FileExportFilter")) { v.add((String) param.get(key)); } } // if no param given, set defaults. if (v.size() == 0) { v.add(sWriterDoc); v.add(sCalcDoc); v.add(sImpressDoc); } // store a file extension sDocTypeExportFilter = new String[v.size()][2]; for (int i = 0; i < v.size(); i++) { // 2do: error routine for wrong given params final String sVContent = v.get(i); StringTokenizer t = new StringTokenizer(sVContent, ","); final String sExt = t.nextToken(); final String sName = t.nextToken(); sDocTypeExportFilter[i][0] = sExt; sDocTypeExportFilter[i][1] = sName; } // get files to load and export // sDocumentPath = (String) param.get("TestDocumentPath"); String sDocumentPath = TestDocument.getUrl(); File f = new File(FileHelper.getJavaCompatibleFilename(sDocumentPath)); // sDocumentPath = f.getAbsolutePath(); // String sFS = System.getProperty("file.separator"); sDocuments = new String[sDocTypeExportFilter.length][]; for (int j = 0; j < sDocTypeExportFilter.length; j++) { FileFilter filter = new FileFilter(sDocTypeExportFilter[j][0]); String[] doc = f.list(filter); sDocuments[j] = new String[doc.length]; for (int i = 0; i < doc.length; i++) { // final String sDocument = FileHelper.appendPath(sDocumentPath, doc[i]); // sDocuments[j][i] = utils.getFullURL(sDocuments[j][i]); sDocuments[j][i] = TestDocument.getUrl(doc[i]); } } } /** * delete all created files on disk */ @After public void after() { // delete the constructed files. // we don't need to delete anything, all is stored in $USER_TREE // for (int i = 0; i < iExportDocCount; i++) // { // final String sDocumentName = "DocExport" + i + ".pdf"; // final String sFilename = FileHelper.appendPath(m_sTempDir, sDocumentName); // File f = new File(FileHelper.getJavaCompatibleFilename(sFilename)); // f.delete(); // } // File f = new File(sProcessIdCommand); // f.delete(); // f = new File(sOfficeMemoryCommand); // f.delete(); } /** * The test function: load documents and save them using the given filters * for each given document type. */ @Test public void loadAndSaveDocuments() { int nOk = 0; int nRunThrough = 0; // At first: // we load the document, there will be some post work in office like late initialisations // we store exact one time the document // so the memory footprint should be right // iterate over all document types for (int k = 0; k < sDocTypeExportFilter.length; k++) { // iterate over all documents of this type for (int i = 0; i < sDocuments[k].length; i++) { final String sDocument = sDocuments[k][i]; final String sExtension = sDocTypeExportFilter[k][1]; // OfficeMemchecker aChecker = new OfficeMemchecker(); // aChecker.setDocumentName(FileHelper.getBasename(sDocument)); // aChecker.setExtension(sExtension); // aChecker.start(); loadAndSaveNTimesDocument(sDocument, 1, sExtension); // nOk += checkMemory(aChecker); // nRunThrough ++; } System.out.println(); System.out.println(); } shortWait(10000); // Now the real test, load document and store 25 times // iterate over all document types for (int k = 0; k < sDocTypeExportFilter.length; k++) { // iterate over all documents of this type for (int i = 0; i < sDocuments[k].length; i++) { final String sDocument = sDocuments[k][i]; final String sExtension = sDocTypeExportFilter[k][1]; OfficeMemchecker aChecker = new OfficeMemchecker(); aChecker.setDocumentName(FileHelper.getBasename(sDocument)); aChecker.setExtension(sExtension); aChecker.start(); loadAndSaveNTimesDocument(sDocument, iExportDocCount, sExtension); aChecker.stop(); final int nConsumMore = aChecker.getConsumMore(); nOk += checkMemory(nConsumMore); nRunThrough++; } System.out.println(); System.out.println(); } System.out.println("Find the output of used 'pmap' here: " + m_aTempDir.getTempDir() + " if test failed."); assertTrue("Office consumes too many memory.", nOk == nRunThrough); } /** * Checks how much memory should consum * @param storageBefore * @return 1 if consum is ok, else 0 */ private int checkMemory(int nConsumMore) { int nAllowed = iAllowMemoryIncrease * iExportDocCount; System.out.println("The Office consumes now " + nConsumMore + "K more memory than at the start of the test; allowed were " + nAllowed + "K."); if (nConsumMore > nAllowed) { System.out.println("ERROR: This is not allowed."); return 0; } System.out.println("OK."); return 1; } /** * load and save exact one document */ private void loadAndSaveNTimesDocument(String _sDocument, int _nCount, String _sStoreExtension) { System.out.println("Document: " + _sDocument); XComponent xComponent = DesktopTools.loadDoc(getMSF(), _sDocument, null); XStorable xStorable = UnoRuntime.queryInterface(XStorable.class, xComponent); if (xStorable != null) { // export each document iExportDocCount times for (int j = 0; j < _nCount; j++) { final String sDocumentName = FileHelper.getBasename(_sDocument) + "_" + j + ".pdf"; final String sFilename = FileHelper.appendPath(m_aTempDir.getOfficeTempDir(), sDocumentName); // String url = utils.getFullURL(sFilename); String url = sFilename; // graphical.FileHelper.getFileURLFromSystemPath(sFilename); try { PropertyValue[] props = new PropertyValue[1]; props[0] = new PropertyValue(); props[0].Name = "FilterName"; // use export filter for this doc type props[0].Value = _sStoreExtension; xStorable.storeToURL(url, props); } catch (com.sun.star.io.IOException e) { fail("Could not store to '" + url + "'"); } } // close the doc XCloseable xCloseable = UnoRuntime.queryInterface(XCloseable.class, xStorable); try { xCloseable.close(true); } catch (com.sun.star.util.CloseVetoException e) { e.printStackTrace(); fail("Cannot close document: test is futile, Office will surely use more space."); } } else { System.out.println("Cannot query for XStorable interface on document '" + _sDocument + "'"); System.out.println(" -> Skipping storage."); } } // ----------------------------------------------------------------------------- private class OfficeMemchecker { /** * After called start() it contains the memory need at startup */ private int m_nMemoryStart; /** * After called stop() it contains the memory usage */ private int m_nMemoryUsage; private String m_sDocumentName; private String m_sExtension; public OfficeMemchecker() { m_nMemoryStart = 0; } public void setDocumentName(String _sDocName) { m_sDocumentName = _sDocName; } public void setExtension(String _sExt) { m_sExtension = _sExt; } public void start() { m_nMemoryStart = getOfficeMemoryUsage(createModeName("start", 0)); } private String createModeName(String _sSub, int _nCount) { StringBuffer aBuf = new StringBuffer(); aBuf.append(_sSub); aBuf.append('_').append(m_sDocumentName).append('_').append(m_sExtension); aBuf.append('_').append(_nCount); return aBuf.toString(); } public void stop() { // short wait for the office to 'calm down' and free some memory shortWait(20000); // wait util memory is not freed anymore. int storageAfter = getOfficeMemoryUsage(createModeName("stop", 0)); int mem = 0; int count = 0; while (storageAfter != mem && count < 10) { count++; mem = storageAfter; storageAfter = getOfficeMemoryUsage(createModeName("stop", count)); shortWait(1000); } m_nMemoryUsage = (storageAfter - m_nMemoryStart); } public int getConsumMore() { return m_nMemoryUsage; } /** * Get the process ID from the Office * @return the Id as String */ private String getOfficeProcessID() { String sProcessIdCommand = FileHelper.appendPath(m_aTempDir.getTempDir(), "getPS"); final String sofficeArg = org.openoffice.test.Argument.get("soffice"); final String sPSGrep = "ps -ef | grep $USER | grep .bin | grep -v grep"; final String sProcessId = sPSGrep.replaceAll("", FileHelper.getJavaCompatibleFilename(sofficeArg)); createExecutableFile(sProcessIdCommand, sProcessId); ProcessHandler processID = new ProcessHandler(sProcessIdCommand); processID.noOutput(); processID.executeSynchronously(); String text = processID.getOutputText(); if (text == null || text.equals("") || text.indexOf(' ') == -1) { fail("Could not determine Office process ID. Check " + sProcessIdCommand); } StringTokenizer aToken = new StringTokenizer(text); // this is not nice, but ps gives the same output on every machine aToken.nextToken(); String id = aToken.nextToken(); return id; } /** * Get the memory usage of the Office in KByte. * @return The memory used by the Office. */ private int getOfficeMemoryUsage(String _sMode) { final String sMemoryMonitor = "pmap |tee | grep total"; String sOfficeMemoryCommand = null; sOfficeMemoryCommand = FileHelper.appendPath(m_aTempDir.getTempDir(), "getPmap"); // sOfficeMemoryCommand = FileHelper.getJavaCompatibleFilename(sOfficeMemoryCommand); String command = sMemoryMonitor.replaceAll("", getOfficeProcessID()); String sPmapOutputFile = FileHelper.appendPath(m_aTempDir.getTempDir(), "pmap_" + _sMode + ".txt"); command = command.replaceAll("", sPmapOutputFile); createExecutableFile(sOfficeMemoryCommand, command); ProcessHandler processID = new ProcessHandler(sOfficeMemoryCommand); processID.noOutput(); processID.executeSynchronously(); int nError = processID.getExitCode(); assertTrue("Execute of " + sOfficeMemoryCommand + " failed", nError == 0); String text = processID.getOutputText(); if (text == null || text.equals("") || text.indexOf(' ') == -1) { fail("Could not determine Office memory usage. Check " + sOfficeMemoryCommand); } StringTokenizer aToken = new StringTokenizer(text); // this works, because the output of pmap is quite standardized. aToken.nextToken(); String mem = aToken.nextToken(); mem = mem.substring(0, mem.indexOf('K')); Integer memory = new Integer(mem); return memory.intValue(); } /** * Write a script file and set its rights to rwxrwxrwx. * @param fileName The name of the created file * @param line The commandline that has to be written inside of the file. */ private void createExecutableFile(String fileName, String line) { final String sChmod = "chmod a+x "; final String bash = "#!/bin/bash"; try { String sFilename = FileHelper.getJavaCompatibleFilename(fileName); PrintWriter fWriter = new PrintWriter(new FileWriter(sFilename)); fWriter.println(bash); fWriter.println(line); fWriter.close(); // change rights to rwxrwxrwx ProcessHandler processID = new ProcessHandler(sChmod + sFilename); processID.noOutput(); processID.executeSynchronously(); int nError = processID.getExitCode(); assertTrue("chmod failed. ", nError == 0); } catch (java.io.IOException e) { } } } /** * Let this thread sleep for some time * @param milliSeconds time to wait in milliseconds. */ public static void shortWait(int milliSeconds) { System.out.println("Wait for: " + milliSeconds + "ms"); try { Thread.sleep(milliSeconds); } catch (java.lang.InterruptedException e) { // ignore } } /** * Own file filter, will just return ok for all files that end with a given * suffix */ private class FileFilter implements FilenameFilter { private String suffix = null; /** * C'tor. * @param suffix The suffix each filename should end with. */ public FileFilter(String suffix) { this.suffix = suffix; } /** * Returns true, if the name of the file has the suffix given to the * c'tor. * @param name The filename that is tested. * @param file Not used. * @return True, if name ends with suffix. */ public boolean accept(File file, String name) { return name.endsWith(suffix); } } private XMultiServiceFactory getMSF() { final XMultiServiceFactory xMSF1 = UnoRuntime.queryInterface(XMultiServiceFactory.class, connection.getComponentContext().getServiceManager()); return xMSF1; } // setup and close connections @BeforeClass public static void setUpConnection() throws Exception { System.out.println("setUpConnection()"); connection.setUp(); } @AfterClass public static void tearDownConnection() throws InterruptedException, com.sun.star.uno.Exception { System.out.println("tearDownConnection()"); connection.tearDown(); } private static final OfficeConnection connection = new OfficeConnection(); }