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