LibreOffice Module test (master)  1
screenshot_test.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <sal/config.h>
11 
12 #include <iostream>
13 
14 #include <test/screenshot_test.hxx>
15 
16 #include <com/sun/star/frame/Desktop.hpp>
18 #include <vcl/abstdlg.hxx>
19 #include <vcl/pngwrite.hxx>
20 #include <vcl/svapp.hxx>
21 #include <vcl/virdev.hxx>
22 #include <vcl/weld.hxx>
23 #include <tools/stream.hxx>
24 
25 
26 namespace {
27  void splitHelpId( const OString& rHelpId, OUString& rDirname, OUString &rBasename )
28  {
29  sal_Int32 nIndex = rHelpId.lastIndexOf( '/' );
30 
31  if( nIndex > 0 )
32  rDirname = OStringToOUString( rHelpId.subView( 0, nIndex ), RTL_TEXTENCODING_UTF8 );
33 
34  if( rHelpId.getLength() > nIndex+1 )
35  rBasename= OStringToOUString( rHelpId.subView( nIndex+1 ), RTL_TEXTENCODING_UTF8 );
36  }
37 }
38 
39 using namespace css;
40 using namespace css::uno;
41 
43 constexpr OUStringLiteral g_aScreenshotDirectory(u"screenshots");
44 
46  : maParent(nullptr, "vcl/ui/screenshotparent.ui", "ScreenShot")
47  , mxParentWidget(maParent.getDialog()->weld_content_area())
48 {
49  if (auto const env = getenv("LO_TEST_LOCALE")) {
50  maCurrentLanguage = OUString::fromUtf8(env);
51  }
52 }
53 
55 {
56 }
57 
59 {
61 
62  mxDesktop = css::frame::Desktop::create( comphelper::getComponentContext(getMultiServiceFactory()) );
63  CPPUNIT_ASSERT_MESSAGE("no desktop!", mxDesktop.is());
64 
65  osl::Directory::create( m_directories.getURLFromWorkdir( g_aScreenshotDirectory)) ;
66 
67  // initialize maKnownDialogs
68  if (maKnownDialogs.empty())
69  {
71  }
72 }
73 
74 void ScreenshotTest::implSaveScreenshot(const BitmapEx& rScreenshot, const OString& rScreenshotId)
75 {
76  OUString aDirname, aBasename;
77  splitHelpId(rScreenshotId, aDirname, aBasename);
78  aDirname = g_aScreenshotDirectory + "/" + aDirname +
79  ( (maCurrentLanguage == "en-US") ? OUString() : "/" + maCurrentLanguage );
80 
81  auto const dirUrl = m_directories.getURLFromWorkdir(aDirname);
82  auto const e = osl::Directory::createPath(dirUrl);
83  if (e != osl::FileBase::E_EXIST) {
84  CPPUNIT_ASSERT_EQUAL_MESSAGE(
85  OString("Failed to create " + OUStringToOString(dirUrl, RTL_TEXTENCODING_UTF8))
86  .getStr(),
87  osl::FileBase::E_None, e);
88  }
89 
90  auto const pngUrl = OUString(dirUrl + "/" + aBasename + ".png");
91  SvFileStream aNew(pngUrl, StreamMode::WRITE | StreamMode::TRUNC);
92  CPPUNIT_ASSERT_MESSAGE(OString("Failed to open <" + OUStringToOString(pngUrl, RTL_TEXTENCODING_UTF8) + ">: " + OString::number(sal_uInt32(aNew.GetErrorCode()))).getStr(), aNew.IsOpen());
93 
94  std::cout << "saving " << pngUrl << ":\n";
95  vcl::PNGWriter aPNGWriter(rScreenshot);
96  aPNGWriter.Write(aNew);
97 }
98 
100 {
101  const BitmapEx aScreenshot(rDialog.createScreenshot());
102 
103  if (!aScreenshot.IsEmpty())
104  {
105  const OString aScreenshotId = rDialog.GetScreenshotId();
106 
107  if (!aScreenshotId.isEmpty())
108  {
109  implSaveScreenshot(aScreenshot, aScreenshotId);
110  }
111  }
112 }
113 
115 {
116  VclPtr<VirtualDevice> xDialogSurface(rDialog.screenshot());
117  const BitmapEx aScreenshot(xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()));
118 
119  if (!aScreenshot.IsEmpty())
120  {
121  const OString aScreenshotId = rDialog.get_help_id();
122  assert(!aScreenshotId.isEmpty());
123  implSaveScreenshot(aScreenshot, aScreenshotId);
124  }
125 }
126 
128 {
129  const mapType::const_iterator aHit = maKnownDialogs.find(rName);
130 
131  if (aHit != maKnownDialogs.end())
132  {
133  return createDialogByID((*aHit).second);
134  }
135 
136  return VclPtr<VclAbstractDialog>();
137 }
138 
140 {
141  const std::vector<OString> aPageDescriptions(rDialog.getAllPageUIXMLDescriptions());
142 
143  if (!aPageDescriptions.empty())
144  {
145  for (size_t a(0); a < aPageDescriptions.size(); a++)
146  {
147  if (rDialog.selectPageByUIXMLDescription(aPageDescriptions[a]))
148  {
149  saveScreenshot(rDialog);
150  }
151  else
152  {
153  CPPUNIT_ASSERT(false);
154  }
155  }
156  }
157  else
158  {
159  saveScreenshot(rDialog);
160  }
161 }
162 
164 {
165  std::unique_ptr<weld::Window> xDialog(rBuilder.create_screenshot_window());
166 
167  auto xTabCtrl = rBuilder.weld_notebook("tabcontrol");
168 
169  int nPages = xTabCtrl ? xTabCtrl->get_n_pages() : 0;
170  if (nPages)
171  {
172  for (int i = 0; i < nPages; ++i)
173  {
174  OString sIdent(xTabCtrl->get_page_ident(i));
175  xTabCtrl->set_current_page(sIdent);
176  if (xTabCtrl->get_current_page_ident() == sIdent)
177  {
178  OString sOrigHelpId(xDialog->get_help_id());
179  // skip empty pages
180  weld::Container* pPage = xTabCtrl->get_page(sIdent);
181  OString sBuildableName(pPage->get_buildable_name());
182  if (!sBuildableName.isEmpty() && !sBuildableName.startsWith("__"))
183  xDialog->set_help_id(pPage->get_help_id());
184  saveScreenshot(*xDialog);
185  xDialog->set_help_id(sOrigHelpId);
186  }
187  else
188  {
189  CPPUNIT_ASSERT(false);
190  }
191  }
192  }
193  else
194  {
195  saveScreenshot(*xDialog);
196  }
197 }
198 
199 void ScreenshotTest::dumpDialogToPath(std::string_view rUIXMLDescription)
200 {
201  if (rUIXMLDescription.empty())
202  return;
203 
204  bool bNonConforming = rUIXMLDescription == "modules/swriter/ui/sidebarstylepresets.ui" ||
205  rUIXMLDescription == "modules/swriter/ui/sidebartheme.ui" ||
206  rUIXMLDescription == "modules/swriter/ui/notebookbar.ui" ||
207  rUIXMLDescription == "modules/scalc/ui/sidebaralignment.ui" ||
208  rUIXMLDescription == "modules/scalc/ui/sidebarcellappearance.ui" ||
209  rUIXMLDescription == "modules/scalc/ui/sidebarnumberformat.ui" ||
210  rUIXMLDescription == "sfx/ui/helpbookmarkpage.ui" ||
211  rUIXMLDescription == "sfx/ui/helpcontentpage.ui" ||
212  rUIXMLDescription == "sfx/ui/helpindexpage.ui" ||
213  rUIXMLDescription == "sfx/ui/helpsearchpage.ui" ||
214  rUIXMLDescription == "sfx/ui/startcenter.ui" ||
215  rUIXMLDescription == "svx/ui/datanavigator.ui" ||
216  rUIXMLDescription == "svx/ui/xformspage.ui" ||
217  rUIXMLDescription == "modules/dbreport/ui/conditionwin.ui";
218  if (bNonConforming) // skip these broken ones
219  return;
220  std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(mxParentWidget.get(), OStringToOUString(rUIXMLDescription, RTL_TEXTENCODING_UTF8)));
221  dumpDialogToPath(*xBuilder);
222 }
223 
225 {
226  for (const auto& rDialog : getKnownDialogs())
227  {
229 
230  if (pDlg)
231  {
232  // known dialog, dump screenshot to path
233  dumpDialogToPath(*pDlg);
234  }
235  else
236  {
237  // unknown dialog, should not happen in this basic loop.
238  // You have probably forgotten to add a case and
239  // implementation to createDialogByID, please do this
240  }
241  }
242 }
243 
244 void ScreenshotTest::processDialogBatchFile(std::u16string_view rFile)
245 {
246  test::Directories aDirectories;
247  const OUString aURL(aDirectories.getURLFromSrc(rFile));
248  SvFileStream aStream(aURL, StreamMode::READ);
249  OString aNextUIFile;
250  const OString aComment("#");
251 
252  while (aStream.ReadLine(aNextUIFile))
253  {
254  if (!aNextUIFile.isEmpty() && !aNextUIFile.startsWith(aComment))
255  {
256  std::cout << "processing " << aNextUIFile << ":\n";
257 
258  // first check if it's a known dialog
260 
261  if (pDlg)
262  {
263  // known dialog, dump screenshot to path
264  dumpDialogToPath(*pDlg);
265  }
266  else
267  {
268  // unknown dialog, try fallback to generic created
269  // Builder-generated instance. Keep in mind that Dialogs
270  // using this mechanism will probably not be layouted well
271  // since the setup/initialization part is missing. Thus,
272  // only use for fallback when only the UI file is available.
273  dumpDialogToPath(aNextUIFile);
274  }
275  }
276  }
277 }
278 
279 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual void setUp() override
URL aURL
css::uno::Reference< css::frame::XDesktop2 > mxDesktop
constexpr OUStringLiteral g_aScreenshotDirectory(u"screenshots")
the target directory for screenshots
sal_Int32 nIndex
mapType maKnownDialogs
the set of known dialogs and their ID for usage in createDialogByID
OUString getURLFromSrc(std::u16string_view rPath) const
static weld::Builder * CreateBuilder(weld::Widget *pParent, const OUString &rUIFile, bool bMobile=false, sal_uInt64 nLOKWindowId=0)
virtual OString GetScreenshotId() const
virtual bool selectPageByUIXMLDescription(const OString &rUIXMLDescription)
virtual ~ScreenshotTest() override
virtual VclPtr< VirtualDevice > screenshot()=0
OUString getURLFromWorkdir(std::u16string_view rPath) const
void saveScreenshot(VclAbstractDialog const &rDialog)
OUString maCurrentLanguage
The current UI language.
virtual void set_help_id(const OString &rName)=0
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
const mapType & getKnownDialogs() const
const access to known dialogs
virtual std::unique_ptr< Notebook > weld_notebook(const OString &id)=0
void processDialogBatchFile(std::u16string_view rFile)
helper to process an input file containing the UXMLDescriptions of the dialogs to dump...
int i
uno_Any a
bool Write(SvStream &rStream)
virtual BitmapEx createScreenshot() const
std::unique_ptr< weld::Container > mxParentWidget
ErrCode const & GetErrorCode() const
Reference< XComponentContext > getComponentContext(Reference< XMultiServiceFactory > const &factory)
virtual void setUp() override
void processAllKnownDialogs()
helper to process all known dialogs
void dumpDialogToPath(weld::Builder &rDialog)
helper method to create and dump a dialog based on Builder contents.
void implSaveScreenshot(const BitmapEx &rScreenshot, const OString &rScreenshotId)
helpers
virtual void registerKnownDialogsByID(mapType &rKnownDialogs)=0
helper method to populate maKnownDialogs, called in setUp().
VclPtr< VclAbstractDialog > createDialogByName(const OString &rName)
Dialog creation for known dialogs by Name (path and UIXMLDescription, *.ui file). ...
virtual OString get_help_id() const =0
virtual VclPtr< VclAbstractDialog > createDialogByID(sal_uInt32 nID)=0
dialog creation for known dialogs by ID.
virtual std::vector< OString > getAllPageUIXMLDescriptions() const
virtual std::unique_ptr< Window > create_screenshot_window()=0
bool IsOpen() const
const css::uno::Reference< css::lang::XMultiServiceFactory > & getMultiServiceFactory() const