1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 package complex.memCheck; 24 25 import com.sun.star.beans.PropertyValue; 26 import com.sun.star.frame.XStorable; 27 import com.sun.star.lang.XComponent; 28 import com.sun.star.lang.XMultiServiceFactory; 29 import com.sun.star.uno.UnoRuntime; 30 import com.sun.star.util.XCloseable; 31 // import complexlib.ComplexTestCase; 32 import helper.ProcessHandler; 33 import java.io.File; 34 // import java.io.FilePermission; 35 import java.io.FileWriter; 36 import java.io.FilenameFilter; 37 import java.io.PrintWriter; 38 import java.util.Enumeration; 39 import java.util.StringTokenizer; 40 import java.util.Vector; 41 import lib.*; 42 import util.DesktopTools; 43 // import util.WriterTools; 44 45 import org.junit.After; 46 import org.junit.AfterClass; 47 import org.junit.Before; 48 import org.junit.BeforeClass; 49 import org.junit.Test; 50 import org.openoffice.test.OfficeConnection; 51 import static org.junit.Assert.*; 52 53 /** 54 * Documents are opened and exported with StarOffice. The memory usage of 55 * StarOffice is monitored and if the usage exceeds the allowed kilobytes, 56 * the test is failed. Used for monitoring the StarOffice process is the 57 * command line tool 'pmap', available on Solaris or Linux. This test will not 58 * run on Windows.<br>Test procedure: every given document type is searched in 59 * the source directory 60 * Needed parameters: 61 * <ul> 62 * <li>"TestDocumentPath" - the path where test documents are located.</li> 63 * <li>"AllowMemoryIncrease" (optional) - the allowed memory increase measured in kByte per exported document. The default is 10 kByte.</li> 64 * <li>"ExportDocCount" (optional) - the amount of exports for each document that is loaded. Is defaulted to 25. 65 * <li>"FileExportFilter" (optional) - a relation between loaded document type and used export filter. Is defaulted to 66 * writer, calc and impress. This parameter can be set with a number to give more than one relation. Example:<br> 67 * "FileExportFilter1=sxw,writer_pdf_Export"<br> 68 * "FileExportFilter2=sxc,calc_pdf_Export"<br> 69 * "FileExportFilter3=sxi,impress_pdf_Export"<br></li> 70 * All parameters are used for iteration over the test document path. 71 * </ul> 72 */ 73 class TempDir 74 { 75 76 private String m_sTempDir; 77 TempDir(String _sTempDir)78 public TempDir(String _sTempDir) 79 { 80 m_sTempDir = _sTempDir; 81 } 82 getOfficeTempDir()83 public String getOfficeTempDir() 84 { 85 return m_sTempDir; 86 } 87 getTempDir()88 public String getTempDir() 89 { 90 final String sTempDir = FileHelper.getJavaCompatibleFilename(m_sTempDir); 91 return sTempDir; 92 } 93 } 94 95 public class CheckMemoryUsage /* extends ComplexTestCase */ 96 97 { 98 99 private final String sWriterDoc = "sxw,writer_pdf_Export"; 100 private final String sCalcDoc = "sxc,calc_pdf_Export"; 101 private final String sImpressDoc = "sxi,impress_pdf_Export"; 102 // private String sProcessIdCommand = null; 103 TempDir m_aTempDir; 104 // private String sFS = null; 105 // private String sMemoryMap1 = null; 106 // private String sMemoryMap2 = null; 107 // private String sDocumentPath = ""; 108 private String[][] sDocTypeExportFilter; 109 private String[][] sDocuments; 110 private int iAllowMemoryIncrease = 10; 111 private int iExportDocCount = 25; 112 /** 113 * The test parameters 114 */ 115 private static TestParameters param = null; 116 117 /** 118 * Get all test methods 119 * @return The test methods. 120 // */ 121 // public String[] getTestMethodNames() { 122 // return new String[] {"loadAndSaveDocuments"}; 123 // } 124 /** 125 * Collect all documnets to load and all filters used for export. 126 */ 127 @Before before()128 public void before() 129 { 130 131 final XMultiServiceFactory xMsf = getMSF(); 132 133 // some Tests need the qadevOOo TestParameters, it is like a Hashmap for Properties. 134 param = new TestParameters(); 135 param.put("ServiceFactory", xMsf); // some qadevOOo functions need the ServiceFactory 136 137 // test does definitely not run on Windows. 138 if (param.get("OperatingSystem").equals("wntmsci")) 139 { 140 System.out.println("Test can only reasonably be executed with a tool that " 141 + "displays the memory usage of StarOffice."); 142 System.out.println("Test does not run on Windows, only on Solaris or Linux."); 143 // in an automatic environment it is better to say, there is no error here. 144 // it is a limitation, but no error. 145 System.exit(0); 146 } 147 148 149 // how many times is every document exported. 150 int count = param.getInt("ExportDocCount"); 151 if (count != 0) 152 { 153 iExportDocCount = count; 154 } 155 156 // get the temp dir for creating the command scripts. 157 // sTempDir = System.getProperty("java.io.tmpdir"); 158 m_aTempDir = new TempDir(util.utils.getOfficeTemp/*Dir*/(xMsf)); 159 160 // get the file extension, export filter connection 161 Enumeration keys = param.keys(); 162 Vector<String> v = new Vector<String>(); 163 while (keys.hasMoreElements()) 164 { 165 String key = (String) keys.nextElement(); 166 if (key.startsWith("FileExportFilter")) 167 { 168 v.add((String) param.get(key)); 169 } 170 } 171 // if no param given, set defaults. 172 if (v.size() == 0) 173 { 174 v.add(sWriterDoc); 175 v.add(sCalcDoc); 176 v.add(sImpressDoc); 177 } 178 // store a file extension 179 sDocTypeExportFilter = new String[v.size()][2]; 180 for (int i = 0; i < v.size(); i++) 181 { 182 // 2do: error routine for wrong given params 183 final String sVContent = v.get(i); 184 StringTokenizer t = new StringTokenizer(sVContent, ","); 185 final String sExt = t.nextToken(); 186 final String sName = t.nextToken(); 187 sDocTypeExportFilter[i][0] = sExt; 188 sDocTypeExportFilter[i][1] = sName; 189 } 190 191 // get files to load and export 192 // sDocumentPath = (String) param.get("TestDocumentPath"); 193 String sDocumentPath = TestDocument.getUrl(); 194 File f = new File(FileHelper.getJavaCompatibleFilename(sDocumentPath)); 195 // sDocumentPath = f.getAbsolutePath(); 196 // String sFS = System.getProperty("file.separator"); 197 sDocuments = new String[sDocTypeExportFilter.length][]; 198 for (int j = 0; j < sDocTypeExportFilter.length; j++) 199 { 200 FileFilter filter = new FileFilter(sDocTypeExportFilter[j][0]); 201 String[] doc = f.list(filter); 202 sDocuments[j] = new String[doc.length]; 203 for (int i = 0; i < doc.length; i++) 204 { 205 // final String sDocument = FileHelper.appendPath(sDocumentPath, doc[i]); 206 // sDocuments[j][i] = utils.getFullURL(sDocuments[j][i]); 207 sDocuments[j][i] = TestDocument.getUrl(doc[i]); 208 } 209 } 210 } 211 212 /** 213 * delete all created files on disk 214 */ 215 @After after()216 public void after() 217 { 218 // delete the constructed files. 219 // we don't need to delete anything, all is stored in $USER_TREE 220 // for (int i = 0; i < iExportDocCount; i++) 221 // { 222 // final String sDocumentName = "DocExport" + i + ".pdf"; 223 // final String sFilename = FileHelper.appendPath(m_sTempDir, sDocumentName); 224 // File f = new File(FileHelper.getJavaCompatibleFilename(sFilename)); 225 // f.delete(); 226 // } 227 // File f = new File(sProcessIdCommand); 228 // f.delete(); 229 // f = new File(sOfficeMemoryCommand); 230 // f.delete(); 231 } 232 233 /** 234 * The test function: load documents and save them using the given filters 235 * for each given document type. 236 */ 237 @Test loadAndSaveDocuments()238 public void loadAndSaveDocuments() 239 { 240 int nOk = 0; 241 int nRunThrough = 0; 242 243 // At first: 244 // we load the document, there will be some post work in office like late initialisations 245 // we store exact one time the document 246 // so the memory footprint should be right 247 248 // iterate over all document types 249 for (int k = 0; k < sDocTypeExportFilter.length; k++) 250 { 251 // iterate over all documents of this type 252 for (int i = 0; i < sDocuments[k].length; i++) 253 { 254 255 final String sDocument = sDocuments[k][i]; 256 final String sExtension = sDocTypeExportFilter[k][1]; 257 258 // OfficeMemchecker aChecker = new OfficeMemchecker(); 259 // aChecker.setDocumentName(FileHelper.getBasename(sDocument)); 260 // aChecker.setExtension(sExtension); 261 // aChecker.start(); 262 263 loadAndSaveNTimesDocument(sDocument, 1, sExtension); 264 265 // nOk += checkMemory(aChecker); 266 // nRunThrough ++; 267 } 268 System.out.println(); 269 System.out.println(); 270 } 271 272 shortWait(10000); 273 274 // Now the real test, load document and store 25 times 275 276 // iterate over all document types 277 for (int k = 0; k < sDocTypeExportFilter.length; k++) 278 { 279 // iterate over all documents of this type 280 for (int i = 0; i < sDocuments[k].length; i++) 281 { 282 283 final String sDocument = sDocuments[k][i]; 284 final String sExtension = sDocTypeExportFilter[k][1]; 285 286 OfficeMemchecker aChecker = new OfficeMemchecker(); 287 aChecker.setDocumentName(FileHelper.getBasename(sDocument)); 288 aChecker.setExtension(sExtension); 289 aChecker.start(); 290 291 loadAndSaveNTimesDocument(sDocument, iExportDocCount, sExtension); 292 293 aChecker.stop(); 294 final int nConsumMore = aChecker.getConsumMore(); 295 296 nOk += checkMemory(nConsumMore); 297 nRunThrough++; 298 } 299 System.out.println(); 300 System.out.println(); 301 } 302 System.out.println("Find the output of used 'pmap' here: " + m_aTempDir.getTempDir() + " if test failed."); 303 assertTrue("Office consumes too many memory.", nOk == nRunThrough); 304 } 305 306 /** 307 * Checks how much memory should consum 308 * @param storageBefore 309 * @return 1 if consum is ok, else 0 310 */ checkMemory(int nConsumMore)311 private int checkMemory(int nConsumMore) 312 { 313 int nAllowed = iAllowMemoryIncrease * iExportDocCount; 314 System.out.println("The Office consumes now " + nConsumMore 315 + "K more memory than at the start of the test; allowed were " 316 + nAllowed + "K."); 317 if (nConsumMore > nAllowed) 318 { 319 System.out.println("ERROR: This is not allowed."); 320 return 0; 321 } 322 System.out.println("OK."); 323 return 1; 324 } 325 326 /** 327 * load and save exact one document 328 */ loadAndSaveNTimesDocument(String _sDocument, int _nCount, String _sStoreExtension)329 private void loadAndSaveNTimesDocument(String _sDocument, int _nCount, String _sStoreExtension) 330 { 331 System.out.println("Document: " + _sDocument); 332 XComponent xComponent = DesktopTools.loadDoc(getMSF(), _sDocument, null); 333 XStorable xStorable = UnoRuntime.queryInterface(XStorable.class, xComponent); 334 if (xStorable != null) 335 { 336 // export each document iExportDocCount times 337 for (int j = 0; j < _nCount; j++) 338 { 339 final String sDocumentName = FileHelper.getBasename(_sDocument) + "_" + j + ".pdf"; 340 final String sFilename = FileHelper.appendPath(m_aTempDir.getOfficeTempDir(), sDocumentName); 341 // String url = utils.getFullURL(sFilename); 342 String url = sFilename; // graphical.FileHelper.getFileURLFromSystemPath(sFilename); 343 try 344 { 345 PropertyValue[] props = new PropertyValue[1]; 346 props[0] = new PropertyValue(); 347 props[0].Name = "FilterName"; 348 // use export filter for this doc type 349 props[0].Value = _sStoreExtension; 350 xStorable.storeToURL(url, props); 351 } 352 catch (com.sun.star.io.IOException e) 353 { 354 fail("Could not store to '" + url + "'"); 355 } 356 } 357 // close the doc 358 XCloseable xCloseable = UnoRuntime.queryInterface(XCloseable.class, xStorable); 359 try 360 { 361 xCloseable.close(true); 362 } 363 catch (com.sun.star.util.CloseVetoException e) 364 { 365 e.printStackTrace(); 366 fail("Cannot close document: test is futile, Office will surely use more space."); 367 } 368 } 369 else 370 { 371 System.out.println("Cannot query for XStorable interface on document '" + _sDocument + "'"); 372 System.out.println(" -> Skipping storage."); 373 } 374 375 } 376 377 // ----------------------------------------------------------------------------- 378 private class OfficeMemchecker 379 { 380 381 /** 382 * After called start() it contains the memory need at startup 383 */ 384 private int m_nMemoryStart; 385 /** 386 * After called stop() it contains the memory usage 387 */ 388 private int m_nMemoryUsage; 389 private String m_sDocumentName; 390 private String m_sExtension; 391 OfficeMemchecker()392 public OfficeMemchecker() 393 { 394 m_nMemoryStart = 0; 395 } 396 setDocumentName(String _sDocName)397 public void setDocumentName(String _sDocName) 398 { 399 m_sDocumentName = _sDocName; 400 } 401 setExtension(String _sExt)402 public void setExtension(String _sExt) 403 { 404 m_sExtension = _sExt; 405 } 406 start()407 public void start() 408 { 409 m_nMemoryStart = getOfficeMemoryUsage(createModeName("start", 0)); 410 } 411 createModeName(String _sSub, int _nCount)412 private String createModeName(String _sSub, int _nCount) 413 { 414 StringBuffer aBuf = new StringBuffer(); 415 aBuf.append(_sSub); 416 aBuf.append('_').append(m_sDocumentName).append('_').append(m_sExtension); 417 aBuf.append('_').append(_nCount); 418 return aBuf.toString(); 419 } 420 stop()421 public void stop() 422 { 423 // short wait for the office to 'calm down' and free some memory 424 shortWait(20000); 425 // wait util memory is not freed anymore. 426 int storageAfter = getOfficeMemoryUsage(createModeName("stop", 0)); 427 int mem = 0; 428 int count = 0; 429 while (storageAfter != mem && count < 10) 430 { 431 count++; 432 mem = storageAfter; 433 storageAfter = getOfficeMemoryUsage(createModeName("stop", count)); 434 shortWait(1000); 435 } 436 m_nMemoryUsage = (storageAfter - m_nMemoryStart); 437 } 438 getConsumMore()439 public int getConsumMore() 440 { 441 return m_nMemoryUsage; 442 } 443 444 /** 445 * Get the process ID from the Office 446 * @return the Id as String 447 */ getOfficeProcessID()448 private String getOfficeProcessID() 449 { 450 String sProcessIdCommand = FileHelper.appendPath(m_aTempDir.getTempDir(), "getPS"); 451 final String sofficeArg = org.openoffice.test.Argument.get("soffice"); 452 final String sPSGrep = "ps -ef | grep $USER | grep <soffice>.bin | grep -v grep"; 453 final String sProcessId = sPSGrep.replaceAll("<soffice>", FileHelper.getJavaCompatibleFilename(sofficeArg)); 454 455 createExecutableFile(sProcessIdCommand, sProcessId); 456 ProcessHandler processID = new ProcessHandler(sProcessIdCommand); 457 processID.noOutput(); 458 processID.executeSynchronously(); 459 String text = processID.getOutputText(); 460 if (text == null || text.equals("") || text.indexOf(' ') == -1) 461 { 462 fail("Could not determine Office process ID. Check " + sProcessIdCommand); 463 } 464 StringTokenizer aToken = new StringTokenizer(text); 465 // this is not nice, but ps gives the same output on every machine 466 aToken.nextToken(); 467 String id = aToken.nextToken(); 468 return id; 469 } 470 471 /** 472 * Get the memory usage of the Office in KByte. 473 * @return The memory used by the Office. 474 */ getOfficeMemoryUsage(String _sMode)475 private int getOfficeMemoryUsage(String _sMode) 476 { 477 final String sMemoryMonitor = "pmap <processID> |tee <pmapoutputfile> | grep total"; 478 String sOfficeMemoryCommand = null; 479 sOfficeMemoryCommand = FileHelper.appendPath(m_aTempDir.getTempDir(), "getPmap"); 480 // sOfficeMemoryCommand = FileHelper.getJavaCompatibleFilename(sOfficeMemoryCommand); 481 String command = sMemoryMonitor.replaceAll("<processID>", getOfficeProcessID()); 482 String sPmapOutputFile = FileHelper.appendPath(m_aTempDir.getTempDir(), "pmap_" + _sMode + ".txt"); 483 command = command.replaceAll("<pmapoutputfile>", sPmapOutputFile); 484 createExecutableFile(sOfficeMemoryCommand, command); 485 486 ProcessHandler processID = new ProcessHandler(sOfficeMemoryCommand); 487 processID.noOutput(); 488 processID.executeSynchronously(); 489 int nError = processID.getExitCode(); 490 assertTrue("Execute of " + sOfficeMemoryCommand + " failed", nError == 0); 491 String text = processID.getOutputText(); 492 if (text == null || text.equals("") || text.indexOf(' ') == -1) 493 { 494 fail("Could not determine Office memory usage. Check " + sOfficeMemoryCommand); 495 } 496 StringTokenizer aToken = new StringTokenizer(text); 497 // this works, because the output of pmap is quite standardized. 498 aToken.nextToken(); 499 String mem = aToken.nextToken(); 500 mem = mem.substring(0, mem.indexOf('K')); 501 Integer memory = new Integer(mem); 502 return memory.intValue(); 503 } 504 505 /** 506 * Write a script file and set its rights to rwxrwxrwx. 507 * @param fileName The name of the created file 508 * @param line The commandline that has to be written inside of the file. 509 */ createExecutableFile(String fileName, String line)510 private void createExecutableFile(String fileName, String line) 511 { 512 final String sChmod = "chmod a+x "; 513 final String bash = "#!/bin/bash"; 514 515 try 516 { 517 String sFilename = FileHelper.getJavaCompatibleFilename(fileName); 518 PrintWriter fWriter = new PrintWriter(new FileWriter(sFilename)); 519 fWriter.println(bash); 520 fWriter.println(line); 521 fWriter.close(); 522 // change rights to rwxrwxrwx 523 ProcessHandler processID = new ProcessHandler(sChmod + sFilename); 524 processID.noOutput(); 525 processID.executeSynchronously(); 526 int nError = processID.getExitCode(); 527 assertTrue("chmod failed. ", nError == 0); 528 } 529 catch (java.io.IOException e) 530 { 531 } 532 } 533 } 534 535 /** 536 * Let this thread sleep for some time 537 * @param milliSeconds time to wait in milliseconds. 538 */ shortWait(int milliSeconds)539 public static void shortWait(int milliSeconds) 540 { 541 System.out.println("Wait for: " + milliSeconds + "ms"); 542 try 543 { 544 Thread.sleep(milliSeconds); 545 } 546 catch (java.lang.InterruptedException e) 547 { // ignore 548 } 549 } 550 551 /** 552 * Own file filter, will just return ok for all files that end with a given 553 * suffix 554 */ 555 private class FileFilter implements FilenameFilter 556 { 557 558 private String suffix = null; 559 560 /** 561 * C'tor. 562 * @param suffix The suffix each filename should end with. 563 */ FileFilter(String suffix)564 public FileFilter(String suffix) 565 { 566 this.suffix = suffix; 567 } 568 569 /** 570 * Returns true, if the name of the file has the suffix given to the 571 * c'tor. 572 * @param name The filename that is tested. 573 * @param file Not used. 574 * @return True, if name ends with suffix. 575 */ accept(File file, String name)576 public boolean accept(File file, String name) 577 { 578 return name.endsWith(suffix); 579 } 580 } 581 getMSF()582 private XMultiServiceFactory getMSF() 583 { 584 final XMultiServiceFactory xMSF1 = UnoRuntime.queryInterface(XMultiServiceFactory.class, connection.getComponentContext().getServiceManager()); 585 return xMSF1; 586 } 587 588 // setup and close connections 589 @BeforeClass setUpConnection()590 public static void setUpConnection() throws Exception 591 { 592 System.out.println("setUpConnection()"); 593 connection.setUp(); 594 } 595 596 @AfterClass tearDownConnection()597 public static void tearDownConnection() 598 throws InterruptedException, com.sun.star.uno.Exception 599 { 600 System.out.println("tearDownConnection()"); 601 connection.tearDown(); 602 } 603 private static final OfficeConnection connection = new OfficeConnection(); 604 } 605