2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 package complex
.memCheck
;
20 import static org
.junit
.Assert
.assertTrue
;
21 import static org
.junit
.Assert
.fail
;
22 import helper
.ProcessHandler
;
25 import java
.io
.FileWriter
;
26 import java
.io
.FilenameFilter
;
27 import java
.io
.PrintWriter
;
28 import java
.util
.StringTokenizer
;
30 import lib
.TestParameters
;
32 import org
.junit
.After
;
33 import org
.junit
.AfterClass
;
34 import org
.junit
.Before
;
35 import org
.junit
.BeforeClass
;
36 import org
.junit
.Test
;
37 import org
.openoffice
.test
.OfficeConnection
;
39 import util
.DesktopTools
;
41 import util
.PropertyName
;
43 import com
.sun
.star
.beans
.PropertyValue
;
44 import com
.sun
.star
.frame
.XStorable
;
45 import com
.sun
.star
.lang
.XComponent
;
46 import com
.sun
.star
.lang
.XMultiServiceFactory
;
47 import com
.sun
.star
.uno
.UnoRuntime
;
48 import com
.sun
.star
.util
.XCloseable
;
51 * Documents are opened and exported with StarOffice. The memory usage of
52 * StarOffice is monitored and if the usage exceeds the allowed kilobytes,
53 * the test is failed. Used for monitoring the StarOffice process is the
54 * command line tool 'pmap', available on Solaris or Linux. This test will not
55 * run on Windows.<br>Test procedure: every given document type is searched in
56 * the source directory
59 * <li>"TestDocumentPath" - the path where test documents are located.</li>
60 * All parameters are used for iteration over the test document path.
66 private final String m_sTempDir
;
68 public TempDir(String _sTempDir
)
70 m_sTempDir
= _sTempDir
;
73 public String
getOfficeTempDir()
78 public String
getTempDir()
80 final String sTempDir
= FileHelper
.getJavaCompatibleFilename(m_sTempDir
);
85 public class CheckMemoryUsage
89 private String
[][] sDocTypeExportFilter
;
90 private String
[][] sDocuments
;
91 // the allowed memory increase measured in kByte per exported document. The default is 10 kByte.
92 // the allowed memory increase per exported document: if the memory increase is higher than this number, the test will fail
93 private static final int iAllowMemoryIncrease
= 10;
94 private int iExportDocCount
= 25;
97 * Collect all documents to load and all filters used for export.
103 final XMultiServiceFactory xMsf
= getMSF();
105 // some Tests need the qadevOOo TestParameters, it is like a Hashmap for Properties.
106 TestParameters param
= new TestParameters();
107 param
.put(PropertyName
.SERVICE_FACTORY
, xMsf
); // some qadevOOo functions need the ServiceFactory
109 // test does definitely not run on Windows.
110 if (param
.get(PropertyName
.OPERATING_SYSTEM
).equals(OSName
.WNTMSCI
))
112 System
.out
.println("Test can only reasonably be executed with a tool that "
113 + "displays the memory usage of StarOffice.");
114 System
.out
.println("Test does not run on Windows, only on Solaris or Linux.");
115 // in an automatic environment it is better to say, there is no error here.
116 // it is a limitation, but no error.
121 // how many times is every document exported.
122 // the amount of exported documents: each loaded document will be written 'ExportDocCount' times
123 iExportDocCount
= 25;
125 // get the temp dir for creating the command scripts.
126 m_aTempDir
= new TempDir(util
.utils
.getOfficeTemp
/*Dir*/(xMsf
));
128 // get the file extension, export filter connection
129 // the import and export filters
130 // store a file extension
131 sDocTypeExportFilter
= new String
[3][2];
132 sDocTypeExportFilter
[0][0] = "sxw";
133 sDocTypeExportFilter
[0][1] = "writer_pdf_Export";
134 sDocTypeExportFilter
[1][0] = "sxc";
135 sDocTypeExportFilter
[1][1] = "calc_pdf_Export";
136 sDocTypeExportFilter
[2][0] = "sxi";
137 sDocTypeExportFilter
[2][1] = "impress_pdf_Export";
139 // get files to load and export
140 String sDocumentPath
= TestDocument
.getUrl();
141 File f
= new File(FileHelper
.getJavaCompatibleFilename(sDocumentPath
));
142 sDocuments
= new String
[sDocTypeExportFilter
.length
][];
143 for (int j
= 0; j
< sDocTypeExportFilter
.length
; j
++)
145 FileFilter filter
= new FileFilter(sDocTypeExportFilter
[j
][0]);
146 String
[] doc
= f
.list(filter
);
147 sDocuments
[j
] = new String
[doc
.length
];
148 for (int i
= 0; i
< doc
.length
; i
++)
150 sDocuments
[j
][i
] = TestDocument
.getUrl(doc
[i
]);
156 * delete all created files on disk
164 * The test function: load documents and save them using the given filters
165 * for each given document type.
168 public void loadAndSaveDocuments()
174 // we load the document, there will be some post work in office like late initialisations
175 // we store exact one time the document
176 // so the memory footprint should be right
178 // iterate over all document types
179 for (int k
= 0; k
< sDocTypeExportFilter
.length
; k
++)
181 // iterate over all documents of this type
182 for (int i
= 0; i
< sDocuments
[k
].length
; i
++)
185 final String sDocument
= sDocuments
[k
][i
];
186 final String sExtension
= sDocTypeExportFilter
[k
][1];
188 loadAndSaveNTimesDocument(sDocument
, 1, sExtension
);
191 System
.out
.println();
192 System
.out
.println();
195 System
.out
.println("Wait for: " + 10000 + "ms");
196 util
.utils
.pause(10000);
198 // Now the real test, load document and store 25 times
200 // iterate over all document types
201 for (int k
= 0; k
< sDocTypeExportFilter
.length
; k
++)
203 // iterate over all documents of this type
204 for (int i
= 0; i
< sDocuments
[k
].length
; i
++)
207 final String sDocument
= sDocuments
[k
][i
];
208 final String sExtension
= sDocTypeExportFilter
[k
][1];
210 OfficeMemchecker aChecker
= new OfficeMemchecker();
211 aChecker
.setDocumentName(FileHelper
.getBasename(sDocument
));
212 aChecker
.setExtension(sExtension
);
215 loadAndSaveNTimesDocument(sDocument
, iExportDocCount
, sExtension
);
218 final int nConsumMore
= aChecker
.getConsumMore();
220 nOk
+= checkMemory(nConsumMore
);
223 System
.out
.println();
224 System
.out
.println();
226 System
.out
.println("Find the output of used 'pmap' here: " + m_aTempDir
.getTempDir() + " if test failed.");
227 assertTrue("Office consumes too many memory.", nOk
== nRunThrough
);
231 * Checks how much memory should consum
232 * @param storageBefore
233 * @return 1 if consum is ok, else 0
235 private int checkMemory(int nConsumMore
)
237 int nAllowed
= iAllowMemoryIncrease
* iExportDocCount
;
238 System
.out
.println("The Office consumes now " + nConsumMore
239 + "K more memory than at the start of the test; allowed were "
241 if (nConsumMore
> nAllowed
)
243 System
.out
.println("ERROR: This is not allowed.");
246 System
.out
.println("OK.");
251 * load and save exact one document
253 private void loadAndSaveNTimesDocument(String _sDocument
, int _nCount
, String _sStoreExtension
)
255 System
.out
.println("Document: " + _sDocument
);
256 XComponent xComponent
= DesktopTools
.loadDoc(getMSF(), _sDocument
, null);
257 XStorable xStorable
= UnoRuntime
.queryInterface(XStorable
.class, xComponent
);
258 if (xStorable
!= null)
260 // export each document iExportDocCount times
261 for (int j
= 0; j
< _nCount
; j
++)
263 final String sDocumentName
= FileHelper
.getBasename(_sDocument
) + "_" + j
+ ".pdf";
264 final String sFilename
= FileHelper
.appendPath(m_aTempDir
.getOfficeTempDir(), sDocumentName
);
265 String url
= sFilename
; // graphical.FileHelper.getFileURLFromSystemPath(sFilename);
268 PropertyValue
[] props
= new PropertyValue
[1];
269 props
[0] = new PropertyValue();
270 props
[0].Name
= "FilterName";
271 // use export filter for this doc type
272 props
[0].Value
= _sStoreExtension
;
273 xStorable
.storeToURL(url
, props
);
275 catch (com
.sun
.star
.io
.IOException e
)
277 fail("Could not store to '" + url
+ "'");
281 XCloseable xCloseable
= UnoRuntime
.queryInterface(XCloseable
.class, xStorable
);
284 xCloseable
.close(true);
286 catch (com
.sun
.star
.util
.CloseVetoException e
)
289 fail("Cannot close document: test is futile, Office will surely use more space.");
294 System
.out
.println("Cannot query for XStorable interface on document '" + _sDocument
+ "'");
295 System
.out
.println(" -> Skipping storage.");
301 private class OfficeMemchecker
305 * After called start() it contains the memory need at startup
307 private int m_nMemoryStart
;
309 * After called stop() it contains the memory usage
311 private int m_nMemoryUsage
;
312 private String m_sDocumentName
;
313 private String m_sExtension
;
315 public OfficeMemchecker()
320 public void setDocumentName(String _sDocName
)
322 m_sDocumentName
= _sDocName
;
325 public void setExtension(String _sExt
)
327 m_sExtension
= _sExt
;
332 m_nMemoryStart
= getOfficeMemoryUsage(createModeName("start", 0));
335 private String
createModeName(String _sSub
, int _nCount
)
337 StringBuffer aBuf
= new StringBuffer();
339 aBuf
.append('_').append(m_sDocumentName
).append('_').append(m_sExtension
);
340 aBuf
.append('_').append(_nCount
);
341 return aBuf
.toString();
346 // short wait for the office to 'calm down' and free some memory
347 System
.out
.println("Wait for: " + 20000 + "ms");
348 util
.utils
.pause(20000);
349 // wait util memory is not freed anymore.
350 int storageAfter
= getOfficeMemoryUsage(createModeName("stop", 0));
353 while (storageAfter
!= mem
&& count
< 10)
357 storageAfter
= getOfficeMemoryUsage(createModeName("stop", count
));
358 System
.out
.println("Wait for: " + 1000 + "ms");
359 util
.utils
.pause(1000);
361 m_nMemoryUsage
= (storageAfter
- m_nMemoryStart
);
364 public int getConsumMore()
366 return m_nMemoryUsage
;
370 * Get the process ID from the Office
371 * @return the Id as String
373 private String
getOfficeProcessID()
375 String sProcessIdCommand
= FileHelper
.appendPath(m_aTempDir
.getTempDir(), "getPS");
376 final String sofficeArg
= org
.openoffice
.test
.Argument
.get("soffice");
377 final String sPSGrep
= "ps -ef | grep $USER | grep <soffice>.bin | grep -v grep";
378 final String sProcessId
= sPSGrep
.replaceAll("<soffice>", FileHelper
.getJavaCompatibleFilename(sofficeArg
));
380 createExecutableFile(sProcessIdCommand
, sProcessId
);
381 ProcessHandler processID
= new ProcessHandler(sProcessIdCommand
);
382 processID
.noOutput();
383 processID
.executeSynchronously();
384 String text
= processID
.getOutputText();
385 if (text
== null || text
.equals("") || text
.indexOf(' ') == -1)
387 fail("Could not determine Office process ID. Check " + sProcessIdCommand
);
389 StringTokenizer aToken
= new StringTokenizer(text
);
390 // this is not nice, but ps gives the same output on every machine
392 String id
= aToken
.nextToken();
397 * Get the memory usage of the Office in KByte.
398 * @return The memory used by the Office.
400 private int getOfficeMemoryUsage(String _sMode
)
402 final String sMemoryMonitor
= "pmap <processID> |tee <pmapoutputfile> | grep total";
403 String sOfficeMemoryCommand
= null;
404 sOfficeMemoryCommand
= FileHelper
.appendPath(m_aTempDir
.getTempDir(), "getPmap");
405 String command
= sMemoryMonitor
.replaceAll("<processID>", getOfficeProcessID());
406 String sPmapOutputFile
= FileHelper
.appendPath(m_aTempDir
.getTempDir(), "pmap_" + _sMode
+ ".txt");
407 command
= command
.replaceAll("<pmapoutputfile>", sPmapOutputFile
);
408 createExecutableFile(sOfficeMemoryCommand
, command
);
410 ProcessHandler processID
= new ProcessHandler(sOfficeMemoryCommand
);
411 processID
.noOutput();
412 processID
.executeSynchronously();
413 int nError
= processID
.getExitCode();
414 assertTrue("Execute of " + sOfficeMemoryCommand
+ " failed", nError
== 0);
415 String text
= processID
.getOutputText();
416 if (text
== null || text
.equals("") || text
.indexOf(' ') == -1)
418 fail("Could not determine Office memory usage. Check " + sOfficeMemoryCommand
);
420 StringTokenizer aToken
= new StringTokenizer(text
);
421 // this works, because the output of pmap is quite standardized.
423 String mem
= aToken
.nextToken();
424 mem
= mem
.substring(0, mem
.indexOf('K'));
425 Integer memory
= Integer
.valueOf(mem
);
426 return memory
.intValue();
430 * Write a script file and set its rights to rwxrwxrwx.
431 * @param fileName The name of the created file
432 * @param line The commandline that has to be written inside of the file.
434 private void createExecutableFile(String fileName
, String line
)
436 final String sChmod
= "chmod a+x ";
437 final String bash
= "#!/bin/bash";
441 String sFilename
= FileHelper
.getJavaCompatibleFilename(fileName
);
442 PrintWriter fWriter
= new PrintWriter(new FileWriter(sFilename
));
443 fWriter
.println(bash
);
444 fWriter
.println(line
);
446 // change rights to rwxrwxrwx
447 ProcessHandler processID
= new ProcessHandler(sChmod
+ sFilename
);
448 processID
.noOutput();
449 processID
.executeSynchronously();
450 int nError
= processID
.getExitCode();
451 assertTrue("chmod failed. ", nError
== 0);
453 catch (java
.io
.IOException e
)
460 * Own file filter, will just return ok for all files that end with a given
463 private class FileFilter
implements FilenameFilter
466 private final String suffix
;
470 * @param suffix The suffix each filename should end with.
472 public FileFilter(String suffix
)
474 this.suffix
= suffix
;
478 * Returns true, if the name of the file has the suffix given to the
480 * @param name The filename that is tested.
481 * @param file Not used.
482 * @return True, if name ends with suffix.
484 public boolean accept(File file
, String name
)
486 return name
.endsWith(suffix
);
490 private XMultiServiceFactory
getMSF()
492 return UnoRuntime
.queryInterface(XMultiServiceFactory
.class, connection
.getComponentContext().getServiceManager());
495 // setup and close connections
497 public static void setUpConnection() throws Exception
499 System
.out
.println("setUpConnection()");
504 public static void tearDownConnection()
505 throws InterruptedException
, com
.sun
.star
.uno
.Exception
507 System
.out
.println("tearDownConnection()");
508 connection
.tearDown();
510 private static final OfficeConnection connection
= new OfficeConnection();