1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 #include <sal/config.h>
14 #include <test/screenshot_test.hxx>
16 #include <com/sun/star/frame/Desktop.hpp>
17 #include <comphelper/processfactory.hxx>
18 #include <vcl/abstdlg.hxx>
19 #include <vcl/filter/PngImageWriter.hxx>
20 #include <vcl/svapp.hxx>
21 #include <vcl/virdev.hxx>
22 #include <vcl/weld.hxx>
23 #include <tools/stream.hxx>
27 void splitHelpId( const OUString
& rHelpId
, OUString
& rDirname
, OUString
&rBasename
)
29 sal_Int32 nIndex
= rHelpId
.lastIndexOf( '/' );
32 rDirname
= rHelpId
.subView( 0, nIndex
);
34 if( rHelpId
.getLength() > nIndex
+1 )
35 rBasename
= rHelpId
.subView( nIndex
+1 );
40 using namespace css::uno
;
42 /// the target directory for screenshots
43 constexpr OUStringLiteral
g_aScreenshotDirectory(u
"screenshots");
45 ScreenshotTest::ScreenshotTest()
46 : maParent(nullptr, "vcl/ui/screenshotparent.ui", "ScreenShot")
47 , mxParentWidget(maParent
.getDialog()->weld_content_area())
49 if (auto const env
= getenv("LO_TEST_LOCALE")) {
50 maCurrentLanguage
= OUString::fromUtf8(env
);
54 ScreenshotTest::~ScreenshotTest()
58 void ScreenshotTest::setUp()
60 test::BootstrapFixture::setUp();
62 mxDesktop
= css::frame::Desktop::create( comphelper::getComponentContext(getMultiServiceFactory()) );
63 CPPUNIT_ASSERT_MESSAGE("no desktop!", mxDesktop
.is());
65 osl::Directory::create( m_directories
.getURLFromWorkdir( g_aScreenshotDirectory
)) ;
67 // initialize maKnownDialogs
68 if (maKnownDialogs
.empty())
70 registerKnownDialogsByID(maKnownDialogs
);
74 void ScreenshotTest::implSaveScreenshot(const BitmapEx
& rScreenshot
, const OUString
& rScreenshotId
)
76 OUString aDirname
, aBasename
;
77 splitHelpId(rScreenshotId
, aDirname
, aBasename
);
78 aDirname
= g_aScreenshotDirectory
+ "/" + aDirname
+
79 ( (maCurrentLanguage
== "en-US") ? OUString() : "/" + maCurrentLanguage
);
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
))
87 osl::FileBase::E_None
, e
);
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());
94 std::cout
<< "saving " << pngUrl
<< ":\n";
95 vcl::PngImageWriter
aPNGWriter(aNew
);
96 aPNGWriter
.write(rScreenshot
);
99 void ScreenshotTest::saveScreenshot(VclAbstractDialog
const & rDialog
)
101 const BitmapEx
aScreenshot(rDialog
.createScreenshot());
103 if (!aScreenshot
.IsEmpty())
105 const OUString aScreenshotId
= rDialog
.GetScreenshotId();
107 if (!aScreenshotId
.isEmpty())
109 implSaveScreenshot(aScreenshot
, aScreenshotId
);
114 void ScreenshotTest::saveScreenshot(weld::Window
& rDialog
)
116 VclPtr
<VirtualDevice
> xDialogSurface(rDialog
.screenshot());
117 const BitmapEx
aScreenshot(xDialogSurface
->GetBitmapEx(Point(), xDialogSurface
->GetOutputSizePixel()));
119 if (!aScreenshot
.IsEmpty())
121 const OUString aScreenshotId
= rDialog
.get_help_id();
122 assert(!aScreenshotId
.isEmpty());
123 implSaveScreenshot(aScreenshot
, aScreenshotId
);
127 VclPtr
<VclAbstractDialog
> ScreenshotTest::createDialogByName(const OString
& rName
)
129 const mapType::const_iterator aHit
= maKnownDialogs
.find(rName
);
131 if (aHit
!= maKnownDialogs
.end())
133 return createDialogByID((*aHit
).second
);
136 return VclPtr
<VclAbstractDialog
>();
139 void ScreenshotTest::dumpDialogToPath(VclAbstractDialog
& rDialog
)
141 const std::vector
<OUString
> aPageDescriptions(rDialog
.getAllPageUIXMLDescriptions());
143 if (!aPageDescriptions
.empty())
145 for (size_t a(0); a
< aPageDescriptions
.size(); a
++)
147 if (rDialog
.selectPageByUIXMLDescription(aPageDescriptions
[a
]))
149 saveScreenshot(rDialog
);
153 CPPUNIT_ASSERT(false);
159 saveScreenshot(rDialog
);
163 void ScreenshotTest::dumpDialogToPath(weld::Builder
& rBuilder
)
165 std::unique_ptr
<weld::Window
> xDialog(rBuilder
.create_screenshot_window());
167 auto xTabCtrl
= rBuilder
.weld_notebook("tabcontrol");
169 int nPages
= xTabCtrl
? xTabCtrl
->get_n_pages() : 0;
172 for (int i
= 0; i
< nPages
; ++i
)
174 OUString
sIdent(xTabCtrl
->get_page_ident(i
));
175 xTabCtrl
->set_current_page(sIdent
);
176 if (xTabCtrl
->get_current_page_ident() == sIdent
)
178 OUString
sOrigHelpId(xDialog
->get_help_id());
180 weld::Container
* pPage
= xTabCtrl
->get_page(sIdent
);
181 OUString
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
);
189 CPPUNIT_ASSERT(false);
195 saveScreenshot(*xDialog
);
199 void ScreenshotTest::dumpDialogToPath(std::string_view rUIXMLDescription
)
201 if (rUIXMLDescription
.empty())
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
220 std::unique_ptr
<weld::Builder
> xBuilder(Application::CreateBuilder(mxParentWidget
.get(), OStringToOUString(rUIXMLDescription
, RTL_TEXTENCODING_UTF8
)));
221 dumpDialogToPath(*xBuilder
);
224 void ScreenshotTest::processAllKnownDialogs()
226 for (const auto& rDialog
: getKnownDialogs())
228 ScopedVclPtr
<VclAbstractDialog
> pDlg(createDialogByID(rDialog
.second
));
232 // known dialog, dump screenshot to path
233 dumpDialogToPath(*pDlg
);
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
244 void ScreenshotTest::processDialogBatchFile(std::u16string_view rFile
)
246 test::Directories aDirectories
;
247 const OUString
aURL(aDirectories
.getURLFromSrc(rFile
));
248 SvFileStream
aStream(aURL
, StreamMode::READ
);
250 const OString
aComment("#");
252 while (aStream
.ReadLine(aNextUIFile
))
254 if (!aNextUIFile
.isEmpty() && !aNextUIFile
.startsWith(aComment
))
256 std::cout
<< "processing " << aNextUIFile
<< ":\n";
258 // first check if it's a known dialog
259 ScopedVclPtr
<VclAbstractDialog
> pDlg(createDialogByName(aNextUIFile
));
263 // known dialog, dump screenshot to path
264 dumpDialogToPath(*pDlg
);
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
);
279 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */