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/pngwrite.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 OString
& rHelpId
, OUString
& rDirname
, OUString
&rBasename
)
29 sal_Int32 nIndex
= rHelpId
.lastIndexOf( '/' );
32 rDirname
= OStringToOUString( rHelpId
.subView( 0, nIndex
), RTL_TEXTENCODING_UTF8
);
34 if( rHelpId
.getLength() > nIndex
+1 )
35 rBasename
= OStringToOUString( rHelpId
.subView( nIndex
+1 ), RTL_TEXTENCODING_UTF8
);
40 using namespace css::uno
;
42 /// the target directory for screenshots
43 constexpr OUStringLiteral
g_aScreenshotDirectory(u
"screenshots");
45 ScreenshotTest::ScreenshotTest()
47 , maParent(nullptr, "vcl/ui/screenshotparent.ui", "ScreenShot")
48 , mxParentWidget(maParent
.getDialog()->weld_content_area())
50 if (auto const env
= getenv("LO_TEST_LOCALE")) {
51 maCurrentLanguage
= OUString::fromUtf8(env
);
55 ScreenshotTest::~ScreenshotTest()
59 void ScreenshotTest::setUp()
61 test::BootstrapFixture::setUp();
63 mxDesktop
= css::frame::Desktop::create( comphelper::getComponentContext(getMultiServiceFactory()) );
64 CPPUNIT_ASSERT_MESSAGE("no desktop!", mxDesktop
.is());
66 osl::Directory::create( m_directories
.getURLFromWorkdir( g_aScreenshotDirectory
)) ;
68 // initialize maKnownDialogs
69 if (maKnownDialogs
.empty())
71 registerKnownDialogsByID(maKnownDialogs
);
75 void ScreenshotTest::implSaveScreenshot(const BitmapEx
& rScreenshot
, const OString
& rScreenshotId
)
77 OUString aDirname
, aBasename
;
78 splitHelpId(rScreenshotId
, aDirname
, aBasename
);
79 aDirname
= g_aScreenshotDirectory
+ "/" + aDirname
+
80 ( (maCurrentLanguage
== "en-US") ? OUString() : "/" + maCurrentLanguage
);
82 auto const dirUrl
= m_directories
.getURLFromWorkdir(aDirname
);
83 auto const e
= osl::Directory::createPath(dirUrl
);
84 if (e
!= osl::FileBase::E_EXIST
) {
85 CPPUNIT_ASSERT_EQUAL_MESSAGE(
86 OString("Failed to create " + OUStringToOString(dirUrl
, RTL_TEXTENCODING_UTF8
))
88 osl::FileBase::E_None
, e
);
91 auto const pngUrl
= OUString(dirUrl
+ "/" + aBasename
+ ".png");
92 SvFileStream
aNew(pngUrl
, StreamMode::WRITE
| StreamMode::TRUNC
);
93 CPPUNIT_ASSERT_MESSAGE(OString("Failed to open <" + OUStringToOString(pngUrl
, RTL_TEXTENCODING_UTF8
) + ">: " + OString::number(sal_uInt32(aNew
.GetErrorCode()))).getStr(), aNew
.IsOpen());
95 std::cout
<< "saving " << pngUrl
<< ":\n";
96 vcl::PNGWriter
aPNGWriter(rScreenshot
);
97 aPNGWriter
.Write(aNew
);
100 void ScreenshotTest::saveScreenshot(VclAbstractDialog
const & rDialog
)
102 const BitmapEx
aScreenshot(rDialog
.createScreenshot());
104 if (!aScreenshot
.IsEmpty())
106 const OString aScreenshotId
= rDialog
.GetScreenshotId();
108 if (!aScreenshotId
.isEmpty())
110 implSaveScreenshot(aScreenshot
, aScreenshotId
);
115 void ScreenshotTest::saveScreenshot(weld::Window
& rDialog
)
117 VclPtr
<VirtualDevice
> xDialogSurface(rDialog
.screenshot());
118 const BitmapEx
aScreenshot(xDialogSurface
->GetBitmapEx(Point(), xDialogSurface
->GetOutputSizePixel()));
120 if (!aScreenshot
.IsEmpty())
122 const OString aScreenshotId
= rDialog
.get_help_id();
123 assert(!aScreenshotId
.isEmpty());
124 implSaveScreenshot(aScreenshot
, aScreenshotId
);
128 VclPtr
<VclAbstractDialog
> ScreenshotTest::createDialogByName(const OString
& rName
)
130 const mapType::const_iterator aHit
= maKnownDialogs
.find(rName
);
132 if (aHit
!= maKnownDialogs
.end())
134 return createDialogByID((*aHit
).second
);
137 return VclPtr
<VclAbstractDialog
>();
140 void ScreenshotTest::dumpDialogToPath(VclAbstractDialog
& rDialog
)
142 const std::vector
<OString
> aPageDescriptions(rDialog
.getAllPageUIXMLDescriptions());
144 if (!aPageDescriptions
.empty())
146 for (size_t a(0); a
< aPageDescriptions
.size(); a
++)
148 if (rDialog
.selectPageByUIXMLDescription(aPageDescriptions
[a
]))
150 saveScreenshot(rDialog
);
154 CPPUNIT_ASSERT(false);
160 saveScreenshot(rDialog
);
164 void ScreenshotTest::dumpDialogToPath(weld::Builder
& rBuilder
)
166 std::unique_ptr
<weld::Window
> xDialog(rBuilder
.create_screenshot_window());
168 auto xTabCtrl
= rBuilder
.weld_notebook("tabcontrol");
170 int nPages
= xTabCtrl
? xTabCtrl
->get_n_pages() : 0;
173 for (int i
= 0; i
< nPages
; ++i
)
175 OString
sIdent(xTabCtrl
->get_page_ident(i
));
176 xTabCtrl
->set_current_page(sIdent
);
177 if (xTabCtrl
->get_current_page_ident() == sIdent
)
179 OString
sOrigHelpId(xDialog
->get_help_id());
181 weld::Container
* pPage
= xTabCtrl
->get_page(sIdent
);
182 OString
sBuildableName(pPage
->get_buildable_name());
183 if (!sBuildableName
.isEmpty() && !sBuildableName
.startsWith("__"))
184 xDialog
->set_help_id(pPage
->get_help_id());
185 saveScreenshot(*xDialog
);
186 xDialog
->set_help_id(sOrigHelpId
);
190 CPPUNIT_ASSERT(false);
196 saveScreenshot(*xDialog
);
200 void ScreenshotTest::dumpDialogToPath(std::string_view rUIXMLDescription
)
202 if (rUIXMLDescription
.empty())
205 bool bNonConforming
= rUIXMLDescription
== "modules/swriter/ui/sidebarstylepresets.ui" ||
206 rUIXMLDescription
== "modules/swriter/ui/sidebartheme.ui" ||
207 rUIXMLDescription
== "modules/swriter/ui/notebookbar.ui" ||
208 rUIXMLDescription
== "modules/scalc/ui/sidebaralignment.ui" ||
209 rUIXMLDescription
== "modules/scalc/ui/sidebarcellappearance.ui" ||
210 rUIXMLDescription
== "modules/scalc/ui/sidebarnumberformat.ui" ||
211 rUIXMLDescription
== "sfx/ui/helpbookmarkpage.ui" ||
212 rUIXMLDescription
== "sfx/ui/helpcontentpage.ui" ||
213 rUIXMLDescription
== "sfx/ui/helpindexpage.ui" ||
214 rUIXMLDescription
== "sfx/ui/helpsearchpage.ui" ||
215 rUIXMLDescription
== "sfx/ui/startcenter.ui" ||
216 rUIXMLDescription
== "svx/ui/datanavigator.ui" ||
217 rUIXMLDescription
== "svx/ui/xformspage.ui" ||
218 rUIXMLDescription
== "modules/dbreport/ui/conditionwin.ui";
219 if (bNonConforming
) // skip these broken ones
221 std::unique_ptr
<weld::Builder
> xBuilder(Application::CreateBuilder(mxParentWidget
.get(), OStringToOUString(rUIXMLDescription
, RTL_TEXTENCODING_UTF8
)));
222 dumpDialogToPath(*xBuilder
);
225 void ScreenshotTest::processAllKnownDialogs()
227 for (const auto& rDialog
: getKnownDialogs())
229 ScopedVclPtr
<VclAbstractDialog
> pDlg(createDialogByID(rDialog
.second
));
233 // known dialog, dump screenshot to path
234 dumpDialogToPath(*pDlg
);
238 // unknown dialog, should not happen in this basic loop.
239 // You have probably forgotten to add a case and
240 // implementation to createDialogByID, please do this
245 void ScreenshotTest::processDialogBatchFile(std::u16string_view rFile
)
247 test::Directories aDirectories
;
248 const OUString
aURL(aDirectories
.getURLFromSrc(rFile
));
249 SvFileStream
aStream(aURL
, StreamMode::READ
);
251 const OString
aComment("#");
253 while (aStream
.ReadLine(aNextUIFile
))
255 if (!aNextUIFile
.isEmpty() && !aNextUIFile
.startsWith(aComment
))
257 std::cout
<< "processing " << aNextUIFile
<< ":\n";
259 // first check if it's a known dialog
260 ScopedVclPtr
<VclAbstractDialog
> pDlg(createDialogByName(aNextUIFile
));
264 // known dialog, dump screenshot to path
265 dumpDialogToPath(*pDlg
);
269 // unknown dialog, try fallback to generic created
270 // Builder-generated instance. Keep in mind that Dialogs
271 // using this mechanism will probably not be layouted well
272 // since the setup/initialization part is missing. Thus,
273 // only use for fallback when only the UI file is available.
274 dumpDialogToPath(aNextUIFile
);
280 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */