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
.copy( 0, nIndex
), RTL_TEXTENCODING_UTF8
);
34 if( rHelpId
.getLength() > nIndex
+1 )
35 rBasename
= OStringToOUString( rHelpId
.copy( 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 maCurrentLanguage
= OUString::fromUtf8(getenv("LO_TEST_LOCALE"));
53 ScreenshotTest::~ScreenshotTest()
57 void ScreenshotTest::setUp()
59 test::BootstrapFixture::setUp();
61 mxDesktop
= css::frame::Desktop::create( comphelper::getComponentContext(getMultiServiceFactory()) );
62 CPPUNIT_ASSERT_MESSAGE("no desktop!", mxDesktop
.is());
64 osl::Directory::create( m_directories
.getURLFromWorkdir( g_aScreenshotDirectory
)) ;
66 // initialize maKnownDialogs
67 if (maKnownDialogs
.empty())
69 registerKnownDialogsByID(maKnownDialogs
);
73 void ScreenshotTest::implSaveScreenshot(const BitmapEx
& rScreenshot
, const OString
& rScreenshotId
)
75 OUString aDirname
, aBasename
;
76 splitHelpId(rScreenshotId
, aDirname
, aBasename
);
77 aDirname
= g_aScreenshotDirectory
+ "/" + aDirname
+
78 ( (maCurrentLanguage
== "en-US") ? OUString() : "/" + maCurrentLanguage
);
80 auto const dirUrl
= m_directories
.getURLFromWorkdir(aDirname
);
81 auto const e
= osl::Directory::createPath(dirUrl
);
82 if (e
!= osl::FileBase::E_EXIST
) {
83 CPPUNIT_ASSERT_EQUAL_MESSAGE(
85 "Failed to create " + dirUrl
, RTL_TEXTENCODING_UTF8
).getStr(),
86 osl::FileBase::E_None
, e
);
89 auto const pngUrl
= OUString(dirUrl
+ "/" + aBasename
+ ".png");
90 SvFileStream
aNew(pngUrl
, StreamMode::WRITE
| StreamMode::TRUNC
);
91 CPPUNIT_ASSERT_MESSAGE(OUStringToOString("Failed to open <" + pngUrl
+ ">: " + OUString::number(sal_uInt32(aNew
.GetErrorCode())), RTL_TEXTENCODING_UTF8
).getStr(), aNew
.IsOpen());
93 std::cout
<< "saving " << pngUrl
<< ":\n";
94 vcl::PNGWriter
aPNGWriter(rScreenshot
);
95 aPNGWriter
.Write(aNew
);
98 void ScreenshotTest::saveScreenshot(VclAbstractDialog
const & rDialog
)
100 const BitmapEx
aScreenshot(rDialog
.createScreenshot());
102 if (!aScreenshot
.IsEmpty())
104 const OString aScreenshotId
= rDialog
.GetScreenshotId();
106 if (!aScreenshotId
.isEmpty())
108 implSaveScreenshot(aScreenshot
, aScreenshotId
);
113 void ScreenshotTest::saveScreenshot(weld::Window
& rDialog
)
115 VclPtr
<VirtualDevice
> xDialogSurface(rDialog
.screenshot());
116 const BitmapEx
aScreenshot(xDialogSurface
->GetBitmapEx(Point(), xDialogSurface
->GetOutputSizePixel()));
118 if (!aScreenshot
.IsEmpty())
120 const OString aScreenshotId
= rDialog
.get_help_id();
121 assert(!aScreenshotId
.isEmpty());
122 implSaveScreenshot(aScreenshot
, aScreenshotId
);
126 VclPtr
<VclAbstractDialog
> ScreenshotTest::createDialogByName(const OString
& rName
)
128 const mapType::const_iterator aHit
= maKnownDialogs
.find(rName
);
130 if (aHit
!= maKnownDialogs
.end())
132 return createDialogByID((*aHit
).second
);
135 return VclPtr
<VclAbstractDialog
>();
138 void ScreenshotTest::dumpDialogToPath(VclAbstractDialog
& rDialog
)
140 const std::vector
<OString
> aPageDescriptions(rDialog
.getAllPageUIXMLDescriptions());
142 if (!aPageDescriptions
.empty())
144 for (size_t a(0); a
< aPageDescriptions
.size(); a
++)
146 if (rDialog
.selectPageByUIXMLDescription(aPageDescriptions
[a
]))
148 saveScreenshot(rDialog
);
152 CPPUNIT_ASSERT(false);
158 saveScreenshot(rDialog
);
162 void ScreenshotTest::dumpDialogToPath(weld::Builder
& rBuilder
)
164 std::unique_ptr
<weld::Window
> xDialog(rBuilder
.create_screenshot_window());
166 auto xTabCtrl
= rBuilder
.weld_notebook("tabcontrol");
168 int nPages
= xTabCtrl
? xTabCtrl
->get_n_pages() : 0;
171 for (int i
= 0; i
< nPages
; ++i
)
173 OString
sIdent(xTabCtrl
->get_page_ident(i
));
174 xTabCtrl
->set_current_page(sIdent
);
175 if (xTabCtrl
->get_current_page_ident() == sIdent
)
177 OString
sOrigHelpId(xDialog
->get_help_id());
179 weld::Container
* pPage
= xTabCtrl
->get_page(sIdent
);
180 OString
sBuildableName(pPage
->get_buildable_name());
181 if (!sBuildableName
.isEmpty() && !sBuildableName
.startsWith("__"))
182 xDialog
->set_help_id(pPage
->get_help_id());
183 saveScreenshot(*xDialog
);
184 xDialog
->set_help_id(sOrigHelpId
);
188 CPPUNIT_ASSERT(false);
194 saveScreenshot(*xDialog
);
198 void ScreenshotTest::dumpDialogToPath(const OString
& rUIXMLDescription
)
200 if (rUIXMLDescription
.isEmpty())
203 bool bNonConforming
= rUIXMLDescription
== "modules/swriter/ui/sidebarstylepresets.ui" ||
204 rUIXMLDescription
== "modules/swriter/ui/sidebartheme.ui" ||
205 rUIXMLDescription
== "modules/swriter/ui/notebookbar.ui" ||
206 rUIXMLDescription
== "modules/scalc/ui/sidebaralignment.ui" ||
207 rUIXMLDescription
== "modules/scalc/ui/sidebarcellappearance.ui" ||
208 rUIXMLDescription
== "modules/scalc/ui/sidebarnumberformat.ui" ||
209 rUIXMLDescription
== "sfx/ui/helpbookmarkpage.ui" ||
210 rUIXMLDescription
== "sfx/ui/helpcontentpage.ui" ||
211 rUIXMLDescription
== "sfx/ui/helpindexpage.ui" ||
212 rUIXMLDescription
== "sfx/ui/helpsearchpage.ui" ||
213 rUIXMLDescription
== "sfx/ui/startcenter.ui" ||
214 rUIXMLDescription
== "svx/ui/datanavigator.ui" ||
215 rUIXMLDescription
== "svx/ui/xformspage.ui" ||
216 rUIXMLDescription
== "modules/dbreport/ui/conditionwin.ui";
217 if (bNonConforming
) // skip these broken ones
219 std::unique_ptr
<weld::Builder
> xBuilder(Application::CreateBuilder(mxParentWidget
.get(), OStringToOUString(rUIXMLDescription
, RTL_TEXTENCODING_UTF8
)));
220 dumpDialogToPath(*xBuilder
);
223 void ScreenshotTest::processAllKnownDialogs()
225 for (const auto& rDialog
: getKnownDialogs())
227 ScopedVclPtr
<VclAbstractDialog
> pDlg(createDialogByID(rDialog
.second
));
231 // known dialog, dump screenshot to path
232 dumpDialogToPath(*pDlg
);
236 // unknown dialog, should not happen in this basic loop.
237 // You have probably forgotten to add a case and
238 // implementation to createDialogByID, please do this
243 void ScreenshotTest::processDialogBatchFile(const OUString
& rFile
)
245 test::Directories aDirectories
;
246 const OUString
aURL(aDirectories
.getURLFromSrc(rFile
));
247 SvFileStream
aStream(aURL
, StreamMode::READ
);
249 const OString
aComment("#");
251 while (aStream
.ReadLine(aNextUIFile
))
253 if (!aNextUIFile
.isEmpty() && !aNextUIFile
.startsWith(aComment
))
255 std::cout
<< "processing " << aNextUIFile
<< ":\n";
257 // first check if it's a known dialog
258 ScopedVclPtr
<VclAbstractDialog
> pDlg(createDialogByName(aNextUIFile
));
262 // known dialog, dump screenshot to path
263 dumpDialogToPath(*pDlg
);
267 // unknown dialog, try fallback to generic created
268 // Builder-generated instance. Keep in mind that Dialogs
269 // using this mechanism will probably not be layouted well
270 // since the setup/initialization part is missing. Thus,
271 // only use for fallback when only the UI file is available.
272 dumpDialogToPath(aNextUIFile
);
278 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */