https://community.kde.org/api.php?action=feedcontributions&user=JHayes&feedformat=atomKDE Community Wiki - User contributions [en]2024-03-28T08:52:25ZUser contributionsMediaWiki 1.40.2https://community.kde.org/index.php?title=Krita/Docs/Unittests_in_Krita&diff=87863Krita/Docs/Unittests in Krita2020-03-20T00:58:00Z<p>JHayes: Correct typo</p>
<hr />
<div>== What is a unit test is and why is it needed? ==<br />
<br />
'''Wiki:''' A unit test is a piece of code that automatically checks if your class or subsystem works correctly. The goal of unit testing is to isolate each part of the program and show that the individual parts are correct. A unit test provides a strict, written contract that the piece of code must satisfy. As a result, it affords several benefits.<ref name="wiki">https://en.wikipedia.org/wiki/Unit_testing</ref> <br />
<br />
'''Comment:''' In other words unit testing allows the developer to verify if his initial design decisions has been implemented correctly and all the corner-cases are handled correctly.<br />
<br />
In Krita Project we use unit tests for several purposes. Not all of them work equally good, but all together they help developing a lot.<br />
<br />
=== Debugging of new code ===<br />
<br />
'''Wiki:''' Unit testing finds problems early in the development cycle. This includes both bugs in the programmer's implementation and flaws or missing parts of the specification for the unit. The process of writing a thorough set of tests forces the author to think through inputs, outputs, and error conditions, and thus more crisply define the unit's desired behavior. The cost of finding a bug before coding begins or when the code is first written is considerably lower than the cost of detecting, identifying, and correcting the bug later; bugs may also cause problems for the end-users of the software.<ref name="wiki" /><br />
<br />
'''Comment:''' Krita is a big project and has numerous subsystems that communicate with each other in complicated ways. It makes debugging and testing new code in the application itself difficult. What is more, just compiling and running the entire application to check a one-line change in a small class is very time-consuming. So when writing a new subsystem we usually split it into smaller parts (classes) and test each of them individually. Testing a single class in isolation helps to catch all the corner-cases in the class public interface, e.g. "what happens if we pass 0 here instead of a valid pointer?" or "what if the index we just gave to this method is invalid?"<br />
<br />
=== Changing/refactoring existing code ===<br />
<br />
'''Wiki:''' Unit testing allows the programmer to refactor code or upgrade system libraries at a later date, and make sure the module still works correctly (e.g., in regression testing). The procedure is to write test cases for all functions and methods so that whenever a change causes a fault, it can be quickly identified. Unit tests detect changes which may break a design contract.<ref name="wiki" /><br />
<br />
'''Comment:''' Imagine someone decides to refactor the code you wrote a year ago. How would he know whether his changes didn't break anything in the internal class structure? Even if he/she asks you, how would you know if the changes to a year-old class, whose details are already forgotten, are valid?<br />
<br />
=== Automated regression testing ===<br />
<br />
Ideally, unit tests should also facilitate automated regression catching (ask [https://build.kde.org/job/calligra%20calligra-2.9%20stable-qt4/ Jenkins]). But at the moment some of Krita unit tests are not stable enough to do the trick. They do straightforward QImage comparisons, so the test results can depend no only on version of the libraries installed, but also on build options and even type of CPU the tests are run on. In the future we plan to split such "unstable" unit test into a separate group and don't run them on Jenkins.<br />
<br />
== When to write a unit test? ==<br />
<br />
Ideally a unit test should be written for any new class that implements some logic and provides any kind of public interface. It is especially true if this public interface is going to be used more that one client-class.<br />
<br />
== What should unit test include? ==<br />
<br />
* corner cases. E.g. what happens if we request merging of two layers, one of which has Inherit Alpha option enabled? What properties and composition mode the final layer should have? Answers to these questions should be given and tested in the unit test.<br />
<br />
* non-obvious design decisions. E.g. if a paint device has a non-transparent default pixel, then its ```exactBounds()``` returns the rect, not smaller that the size of the image, even though technically the device might be empty.<br />
<br />
== How to write a unittest? ==<br />
<br />
Suppose you want to write a unittest for kritaimage library. You need to perform just a few steps:<br />
<br />
<ol><br />
<li>Add files for the test class into ./image/tests/ directory:<br />
<br />
<br>kis_some_class_test.h<br />
<syntaxhighlight lang="cpp"><br />
#ifndef __KIS_SOME_CLASS_TEST_H<br />
#define __KIS_SOME_CLASS_TEST_H<br />
<br />
#include <QtTest/QtTest><br />
<br />
class KisSomeClassTest : public QObject<br />
{<br />
Q_OBJECT<br />
private Q_SLOTS:<br />
void test();<br />
};<br />
<br />
#endif /* __KIS_SOME_CLASS_TEST_H */</syntaxhighlight><br />
<br />
<br>kis_some_class_test.cpp<br />
<syntaxhighlight lang="cpp"><br />
#include "kis_some_class_test.h"<br />
<br />
#include <QTest><br />
<br />
void KisSomeClassTest::test()<br />
{<br />
}<br />
<br />
QTEST_MAIN(KisSomeClassTest, GUI)</syntaxhighlight><br />
<br></li><br />
<br />
<li>Modify ./image/tests/CMakeLists.txt to include your new test class<br><br />
<br />
<syntaxhighlight lang="cmake"><br />
...<br />
########### next target ###############<br />
set(kis_some_class_test_SRCS kis_some_class_test.cpp )<br />
kde4_add_unit_test(KisSomeClassTest TESTNAME kritaimage-some_class_test ${kis_some_class_test_SRCS})<br />
target_link_libraries(KisSomeClassTest ${KDE4_KDEUI_LIBS} kritaimage ${QT_QTTEST_LIBRARY})<br />
...<br />
</syntaxhighlight><br />
<br></li><br />
<br />
<li>Write your test. You can use any macro commands provided by Qt (QVERIFY, QCOMPARE or QBENCHMARK).<br />
<syntaxhighlight lang="cpp"><br />
void KisSomeClassTest::test()<br />
{<br />
QString cat("cat");<br />
QString dog("dog");<br />
<br />
QVERIFY(cat != dog);<br />
QCOMPARE(cat, "cat");<br />
}</syntaxhighlight><br />
<br></li><br />
<br />
<li> Run your test by running an executable in ./image/test/ folder </li><br />
</ol><br />
<br />
== Krita-specific testing utils ==<br />
=== Fetching reference images ===<br />
All the testing files/images are usually stored in the test's data folder (e.g. ./krita/image/tests/data/). But there are some files which are used throughout all the unit tests (e.g. lena.png). These files are stored in the global folder ./krita/sdk/tests/data/. If you want to access any file, just use '''TestUtil::fetchDataFileLazy'''. It first searches the file in the local test's folder and if nothing is found checks the global folder.<br />
<br />
Example:<br />
<syntaxhighlight lang="cpp"><br />
QImage refImage(TestUtil::fetchDataFileLazy("lena.png"));<br />
QVERIFY(!refImage.isNull());<br />
</syntaxhighlight><br />
<br />
=== Compare test result against a reference QImage ===<br />
<br />
There are two helper functions to compare a given QImage against an image saved in the data folder.<br />
<br />
<syntaxhighlight lang="cpp"><br />
bool TestUtil::checkQImage(const QImage &image, const QString &testName,<br />
const QString &prefix, const QString &name,<br />
int fuzzy = 0, int fuzzyAlpha = -1, int maxNumFailingPixels = 0);<br />
bool TestUtil::checkQImageExternal(const QImage &image, const QString &testName,<br />
const QString &prefix, const QString &name,<br />
int fuzzy = 0, int fuzzyAlpha = -1, int maxNumFailingPixels = 0);<br />
</syntaxhighlight><br />
<br />
The functions search for a png file with path<br />
<syntaxhighlight lang="text"><br />
./tests/data/<testName>/<prefix>/<prefix>_<name>.png<br />
# or without a subfolder<br />
./tests/data/<testName>/<prefix>_<name>.png<br />
</syntaxhighlight><br><br />
<br />
The supplied QImage is compared against the saved PNG, and the result is returned to the caller. If the images do not coincide, two images are dumped into the current directory: one with actual result and another with what is expected.<br />
<br />
The second version of the function is different. It searches the image in "an external repository". The point is that PNG images occupy quite a lot of space and bloat the repository size. So we decided to put all the images that are big enough (>10KiB) into an external SVN repository. To configure an external test files repository on your computer, please do the following:<br />
<br />
<ol><br />
<li>Checkout the data repository:<br />
<br />
<syntaxhighlight lang="bash"><br />
# create the tests data folder and enter it<br />
mkdir ~/testsdata<br />
cd ~/testsdata<br />
<br />
#checkout the extra repository<br />
svn checkout svn+ssh://svn@svn.kde.org/home/kde/trunk/tests/kritatests<br />
</syntaxhighlight><br><br />
</li><br />
<br />
<li>Add environment variable pointing to your repository to your ~/.bashrc<br />
<syntaxhighlight lang="bash"><br />
export KRITA_UNITTESTS_DATA_DIR= ~/testsdata/kritatests/unittests<br />
</syntaxhighlight><br><br />
</li><br />
<br />
<li>Use '''TestUtil::checkQImageExternal''' in your unittest and it will fetch data from the external source. If an external repository is not found then the test is considered "passed".</li><br />
</ol><br />
<br />
=== QImageBasedTest for complex actions ===<br />
<br />
Sometimes you need to test some complex actions like cropping or transforming the whole image. The main problem of such action is that it should work correctly with any kind of layer or mask, e.g. KisCloneLayer, KisGroupLayer or even KisSelectionMask. To facilitate such complex testing conditions, Krita provides a special class '''QImageBasedTest'''. It helps you to create a really complex image and check the contents of its layers. You can find the best example of its usage in '''KisProcessingsTest'''. Basically, to use this class, one should derive it's own testing class from it, and call a set of callbacks, which do all the work. Let's consider the code from KisProcessingsTest:<br />
<br />
<syntaxhighlight lang="cpp"><br />
// override QImageBasedTest class<br />
class BaseProcessingTest : public TestUtil::QImageBasedTest<br />
{<br />
public:<br />
BaseProcessingTest()<br />
: QImageBasedTest("processings")<br />
{<br />
}<br />
<br />
// The method is called by test cases. If the test fails, a set of PNG images<br />
// is saved into working directory<br />
void test(const QString &testname, KisProcessingVisitorSP visitor) {<br />
<br />
// create an image and regenerate its projection<br />
KisSurrogateUndoStore *undoStore = new KisSurrogateUndoStore();<br />
KisImageSP image = createImage(undoStore);<br />
image->initialRefreshGraph();<br />
<br />
// check if the image is correct before testing anything<br />
QVERIFY(checkLayersInitial(image));<br />
<br />
// do the action we are trying to test<br />
KisProcessingApplicator applicator(image, image->root(),<br />
KisProcessingApplicator::RECURSIVE);<br />
<br />
applicator.applyVisitor(visitor);<br />
applicator.end();<br />
image->waitForDone();<br />
<br />
// check the result, and dump images if something went wrong<br />
QVERIFY(checkLayers(image, testname));<br />
<br />
// Check if undo(!) works correctly<br />
undoStore->undo();<br />
image->waitForDone();<br />
<br />
if (!checkLayersInitial(image)) {<br />
qWarning() << "NOTE: undo is not completely identical "<br />
<< "to the original image. Falling back to "<br />
<<"projection comparison";<br />
QVERIFY(checkLayersInitialRootOnly(image));<br />
}<br />
}<br />
};<br />
</syntaxhighlight><br />
<br />
=== MaskParent object ===<br />
'''TestUtil::MaskParent''' is a simple class that, in its constructor, creates an RGB8 image with a single paint layer, which you can use for further testing. The image and the layer can be accessed as simple member variables.<br />
<br />
Example:<br />
<syntaxhighlight lang="cpp"><br />
void KisMaskTest::testCreation()<br />
{<br />
// create an image and a simple layer<br />
TestUtil::MaskParent p;<br />
<br />
// create a mask and attach its selection to the created layer<br />
TestMaskSP mask = new TestMask;<br />
mask->initSelection(p.layer);<br />
<br />
QCOMPARE(mask->extent(), QRect(0,0,512,512));<br />
QCOMPARE(mask->exactBounds(), QRect(0,0,512,512));<br />
}<br />
</syntaxhighlight><br />
<br />
<br />
<references/></div>JHayeshttps://community.kde.org/index.php?title=Guidelines_and_HOWTOs/Submit_an_icon&diff=86693Guidelines and HOWTOs/Submit an icon2019-11-17T17:47:24Z<p>JHayes: Problem saving Breeze Icon design guidelines link change</p>
<hr />
<div>= Get the Breeze Icons git repository =<br />
If you already have a KDE development environment set up using the <code>kdesrc-build</code> tool (see https://community.kde.org/Get_Involved/development#One-time_setup:_your_development_environment), then you can get breeze-icons by doing the following:<br />
{{Input|1=<nowiki><br />
kdesrc-build breeze-icons<br />
</nowiki>}}<br />
<br />
If not, and you aren't interested in setting one up right now, here's how to proceed anyway:<br />
<br />
First you need to get the <code>git</code> command line tool, which allows you to interact with repositories of source code (and icons!). Open a terminal program such as Konsole, which is shipped by default in most KDE Linux distributions. In the terminal window, type one of the following commands (depending on which Linux distribution you're using) and then press the enter or return key:<br />
<br />
* Arch/Antergos/Manjaro: <code>sudo pacman -S git</code><br />
* Fedora: <code>sudo dnf install git</code><br />
* KDE Neon/Kubuntu/Ubuntu/Debian: <code>sudo apt install git</code><br />
* openSUSE Leap & Tumbleweed: <code>sudo zypper install git</code><br />
<br /><br />
Now for each line below, type the commands and press the enter or return key:<br />
{{Input|1=<nowiki><br />
mkdir -p ~/kde/src/<br />
cd ~/kde/src/<br />
git clone git://anongit.kde.org/breeze-icons.git<br />
</nowiki>}}<br />
<br />
You have just "cloned" the Breeze Icons git repository to <tt>[your home folder]/kde/src/breeze-icons/</tt>, which is where it now lives. You can now get on with editing an existing icon or submitting a new one. Explore the folder structure inside <tt>[your home folder]/kde/src/breeze-icons/</tt> a bit to get a feel for the structure of the repository--where things live, what kinds of icons go where, and so on. The [https://hig.kde.org/style/icons/index.html Breeze Icon design guidelines] offer more information, and offer helpful guidelines regarding the style you should match. Always follow the [https://hig.kde.org/style/icons/index.html Breeze Icon design guidelines]!<br />
<br />
KDE icons are SVG files. We recommend the use of Inkscape or another free, open-source vector graphics editor for maximum compatibility.<br />
<br />
<br />
= How to edit an existing icon =<br />
Many KDE icons come in multiple versions that are optimized for various sizes, so once you're done editing one version, make sure to edit the others accordingly. For example, if you edit <tt>icons/actions/16/document-open-recent.svg</tt>, you will also need to edit the versions of that file present in <tt>icons/actions/22</tt>, <tt>icons/actions/16</tt>, <tt>icons/actions/24</tt>, and <tt>icons/actions/32</tt>.<br />
<br />
<br />
= How to create a new icon =<br />
To submit a new icon, first create it in your vector graphics editor, and then once you're done, save it to the appropriate place in the <tt>breeze-icons</tt> repository. For example if you are submitting a new app icon, you would save it inside <tt>[your home folder]/kde/src/breeze-icons/icons/apps/48/</tt>. If you are submitting an icon of a type that comes in multiple sizes (for example, an action icon), be sure to save each version to the correct location.<br />
<br />
<br />
= Submit your proposal for review =<br />
<br />
Once you have edited some icons and/or saved your proposed new icons to the appropriate places, it's time to submit a patch so that the KDE VDG team can review it. Type the following commands in your terminal window, pressing the enter or return key after each line, as before:<br />
{{Input|1=<nowiki><br />
cd ~/kde/src/breeze-icons/<br />
git add .<br />
git diff --staged > ~/Desktop/icons.patch<br />
</nowiki>}}<br />
<br />
You now have a "patch file" called <tt>icons.patch</tt> sitting on your desktop. You can now submit this file using Phabricator; see https://community.kde.org/Infrastructure/Phabricator#Posting_a_Patch_using_the_website<br />
<br />
Add <tt>#vdg</tt>as the reviewer. Once you've submitted the patch, please post a comment mentioning your full name and email address.<br />
<br />
<br />
= What happens next =<br />
Members of the KDE VDG team will review your patch and propose improvements. This is a conversation that you can and should participate in! If changes are requested, you should make those changes, and re-run the commands that spat out a patch file to generate a new one. Re-submit that new patch file, and keep going until your reviewers are happy. At that point the patch will be landed and you'll officially be a KDE contributor!<br />
<br />
<br />
= Tips and tricks =<br />
This whole business of generating patch files gets tedious after a while, doesn't it? Instead, consider setting up the <code>arc</code> command-line tool, which makes it much easier to submit patches. To do that, see https://community.kde.org/Infrastructure/Phabricator#Using_Arcanist_to_post_patches<br />
<br />
Also, it's helpful if you go through the full development environment setup (https://community.kde.org/Get_Involved/development#One-time_setup:_your_development_environment) This will allow you to compile the tests in the Breeze Icons repo and then run them:<br />
{{Input|1=<nowiki><br />
kdesrc-build breeze-icons<br />
cd ~/kde/build/breeze-icons/<br />
ctest<br />
</nowiki>}}<br />
<br />
See [[Guidelines and HOWTOs/Icon Workflow Tips]] for more information about size optimization, embedding stylesheets into monochrome icons, and making compilation images for putting into your review request.</div>JHayes