개발/Cpp 단위 테스트

    From The Document Foundation Wiki
    This page is a translated version of the page Development/Cpp Unit Tests and the translation is 94% complete.
    Outdated translations are marked like this.
    Other languages:

    CppUnit

    LibreOffice는 C++ 단위 테스트에 CppUnit 프레임워크를 사용합니다. CppUnit은 JUnit 프레임워크의 C++ 포트이며 여러번 포크된 포트입니다. LibreOffice는 현재 마지막 활성 포크인 자체 fork of CppUnit를 유지합니다.

    Xisco Faulí의 FOSDEM 2021에서의 발표, LibreOffice QA - how to write your first test에서는 테스트 생성 과정을 설명합니다.

    LibreOffice 코드의 단위 테스트 유형

    LibreOffice에는 여러가지 유형의 단위 테스트들이 있습니다; 어떤 유형을 사용해야 할지 잘 모르겠다면 LibreOffice 개발 메일링 리스트 또는 IRC에 문의하세요 - https://www.libreoffice.org/community/developers/의 "Talk to developers"를 참조하세요.

    하나의 기능/메서드 테스트

    기능/메서드를 취하여 다양한 데이터를 공급하고 결과가 정확하다고 주장하세요.

    예시: https://cgit.freedesktop.org/libreoffice/core/tree/sal/qa/rtl/strings/test_oustring_concat.cxx#n57test::oustring::StringConcat::checkConcat()

    Import 테스트

    Import 테스트를 위해, 두 가지가 필요합니다:

    • 테스트 문서 - 처음에 버그를 표시했던 문서의 최소화된 버전입니다. 기밀 데이터를 피하기 위해 문서를 처음부터 다시 생성하는 것이 가장 좋습니다. 문서가 저장되는 영역(일반적으로 'data/'의 하위 디렉토리)에서 유사한 테스트들을 확인합니다.
    • 테스트 그 자체

    그 후 import 테스트는 먼저 문서를 로드하며, 당신은 고쳐진 속성을 정확하게 주장하세요.

    예시: https://cgit.freedesktop.org/libreoffice/core/tree/sw/qa/extras/rtfimport/rtfimport.cxx#n606testFdo50665

    Export 테스트

    Export 테스트는 실제로 두 번 다시 import를 통해 Import 테스트를 확장합니다.

    • 첫 번째로, 문서를 읽은 후, 테스트할 필터를 사용하여 저장합니다.
    • 두 번째로, 저장된 결과를 다시 읽어 저장 작업이 올바르게 수행되었는지 확인할 수 있습니다.

    저장된 결과를 확인하는 것은 import test(위 참조)에서와 같은 방법으로 수행되거나 assertXPath 방법(아래 참조)을 사용하여 수행될 수 있습니다.

    예시: https://cgit.freedesktop.org/libreoffice/core/tree/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx#n115testDMLSolidfillAlpha

    assertXPath 테스트

    대부분의 경우, XML에서 작동합니다:

    • ODF와 OOXML은 컨테이너의 XML입니다.
    • 데이터를 XML로 덤프합니다(다양한 디버깅 결과 - Writer 레이아웃의 덤프, EMF/WMF 구조, XShapes 등 )

    그런 다음, 데이터를 DOM으로 로드하고, XPath 식을 통해 다양한 속성을 주장합니다.

    예시: https://cgit.freedesktop.org/libreoffice/core/tree/sw/qa/extras/ooxmlexport/ooxmlexport6.cxx#n610testBehinddoc

    EMF/WMF 예시: https://cgit.freedesktop.org/libreoffice/core/tree/emfio/qa/cppunit/wmf/wmfimporttest.cxx

    비트맵을 덤핑하고 결과를 주장하는 단위 테스트

    EMF+는 EMF 코멘트에 저장되며 나중에 렌더링됩니다. 이를 테스트하려면 비트맵으로 덤프하고 다양한 픽셀에 올바른 값이 있다고 주장해야 합니다.

    비트맵을 먼저 덤프하려면(주장할 내용을 정확히 확인하기 위해), CPPCANVAS_DEBUG_EMFPLUS_DUMP_TO=/tmp/file.png를 내보냅니다. 그러면 단위 테스트를 실행할 때 비트맵은 /tmp/file.png에 저장됩니다.

    예시: https://cgit.freedesktop.org/libreoffice/core/tree/cppcanvas/qa/extras/emfplus/emfplus.cxx

    레이아웃 덤프 테스트

    이것은 writer에게만 해당됩니다. 레이아웃 엔진에 의해 문서가 어떻게 배치되는지 테스트해야 하는 경우도 있습니다 - 예를 들어, 이전에 2페이지에 표시되던 일부 텍스트가 3페이지에 표시되어야 합니다.

    이러한 테스트는 파일을 가져온 다음, 레이아웃이 XML로 덤프되도록 작동합니다.

    예시: https://cgit.freedesktop.org/libreoffice/core/tree/sw/qa/extras/rtfimport/rtfimport.cxx#n755testFdo52052

    XShape 테스트

    이것은 차트2 또는 Impress에서 쉽게 사용될 수 있습니다. view의 구조(XShapes 계층으로 표시됨)는 XML로 덤프된 다음, 참조 덤프(신뢰성이 낮음 - 덤프 메서드가 나중에 번경될 수 있으므로)와 비교되거나 assertXPath(새 메서드)를 사용하여 확인됩니다.

    예시 (참조): https://cgit.freedesktop.org/libreoffice/core/tree/chart2/qa/extras/xshape/chart2xshape.cxx#n80Chart2XShapeTest::testFdo75075()

    예시 (assertXPath): https://cgit.freedesktop.org/libreoffice/core/tree/chart2/qa/extras/xshape/chart2xshape.cxx#n144Chart2XShapeTest::testTdf76649TrendLineBug()

    복잡한 scenario 테스트

    버그가 다시 재생되기 위해서는 몇 가지 단계가 필요할 수 있습니다. 그것조차도 절차가 신뢰할 수 있을 때 테스트 가능합니다.

    이러한 경우, 일반적으로 단계들이 추가된 import 테스트(텍스트를 선택하거나 그것에 일부 작업을 작동하는 것 등과 같은)가 수행되어야 합니다.

    LibreOfficeKit (LOK) 테스트

    어떤 기능(키를 누르는 것과 같은)은 LibreOfficeKit API를 통해서 가능하지만, UNO 또는 내부 API를 통해서는 가능하지 않습니다.(예를 들어 Writer에서 SwEditWin은 가시성 관점에서 공용 클래스가 아니기 때문입니다.)

    이를 사용하고 싶다면, 문서를 나타내는 UNO object의 구현에 대한 참조를 얻어야 하며, 그러면 구성원 함수를 직접 호출할 수 있습니다.

    성능 테스트

    이러한 테스트들은 make perfcheck의 일부로 실행됩니다. valgrind-dev 패키지가 설치되어야 합니다. 이는 valgrind 로그를 포함하는 workdir/CppunitTest/[testname]/.. 에서 로그 파일을 생성합니다.

    bin/parse-perfcheck.py 를 사용하여 요약 CSV 파일을 생성하기 위해 처리될 수 있습니다.

    예시: sc/qa/perf/scperfobj.cxx

    새로운 테스트 추가

    어떤 경우에는 단위 테스트를 위한 인프라가 도입되지 않았습니다. 이런 경우에는 영감을 얻기 위해 단위 테스트가 많은 영역을 살펴보고, 다음과 같이 새로운 단위 테스트를 생성하세요.

    테스트 전략 발견

    디버거에서 LibreOffice를 실행하고 버그 수정과 관련된 부분에 breakpoint를 삽입할 수 있습니다. 그런 다음, 버그를 발생 시켰던 작업을 수동으로 수행하고 디버거에서 제공하는 backtrace를 검사합니다. 마지막으로 cppunit 테스트에서는 backtrace에서 호출되는 것과 유사한 function을 호출합니다.

    테스트 클래스 정의

    테스트는 test_xxx.cxx 파일에 있는 qa/ 하위 디렉터리에 배치해야 합니다. 기본 테스트 skeleton은 다음과 같습니다:

    // copy here a header from the file TEMPLATE.SOURCECODE.HEADER in the root of the source tree.
    
    #include "sal/config.h"
    #include "sal/precppunit.hxx"
    
    #include "cppunit/TestAssert.h"
    #include "cppunit/TestFixture.h"
    #include "cppunit/extensions/HelperMacros.h"
    #include "cppunit/plugin/TestPlugIn.h"
    
    namespace
    {
    /**
     * Perform tests on MyClass.
     */
    class MyClassTest : public CppUnit::TestFixture
    {
    private:
        /**
         * Tests capability Foo of the class.
         */
        void testFoo();
    
        // Adds code needed to register the test suite
        CPPUNIT_TEST_SUITE(MyClassTest);
        // Declares the method as a test to call
        CPPUNIT_TEST(testFoo);
        // End of test suite definition
        CPPUNIT_TEST_SUITE_END();
    };
    
    void MyClassTest::testFoo()
    {   
        CPPUNIT_ASSERT(true); // do the tests here
    }
    
    // Put the test suite in the registry
    CPPUNIT_TEST_SUITE_REGISTRATION(MyClassTest);
    
    } // namespace
    CPPUNIT_PLUGIN_IMPLEMENT();

    하나 이상의 테스트 function들을 작성하고 클래스의 다양한 기능을 테스트합니다. CppUnit을 사용한 테스트에 대한 자세한 내용은 CppUnit tutorial을 참조하세요.

    빌드시 테스트 실행

    이제 테스트 클래스가 준비되었으므로, 컴파일하여 실행해야 합니다. 즉, toplevel(모듈) 디렉터리에 CppunitTest_module_test.mk (적절한 대체 모듈 및 테스트 이름) 파일이 아직 존재하지 않는다면, 새로 생성합니다.

    # copy here a header from the file TEMPLATE.SOURCECODE.HEADER in the root of the source tree.
    
    $(eval $(call gb_CppunitTest_CppunitTest,module_test))
    
    $(eval $(call gb_CppunitTest_add_exception_objects,module_test, \
    module/qa/cppunit/test_xxx \
    ))
    
    # add a list of all needed libraries here
    $(eval $(call gb_CppunitTest_add_linked_libs,module_test, \
    sal \
    $(gb_STDLIBS) \
    ))
    
    $(eval $(call gb_CppunitTest_set_include,module_test,\
    $$(INCLUDE) \
    -I$(OUTDIR)/inc \
    ))

    또한, Module_module.mk를 수정하고 다음을 추가해야 합니다.(또는 이미 존재하는 경우 리스트에 추가):

    $(eval $(call gb_Module_add_check_targets,module,\
         CppunitTest_module_test \
    ))

    테스트를 빌드하고 실행하려면, make check를 실행하면 됩니다. 빌드에 성공하면 테스트는 자동으로 실행됩니다.

    따라서, 테스트가 실행되면 버그를 수정하면 됩니다.

    한 번에 하나의 테스트 실행

    CPPUNIT_TEST_NAME은 설정해야 하는 환경 변수이며 테스트 이름을 포함합니다. 테스트 이름이 인식되려면 완전히 검증되어야 합니다.

    예시:

    CPPUNIT_TEST_NAME="testOutlineODS testPrintRangeODS" make CppunitTest_sc_subsequent_filters_test2

    Printing to stderr is hidden if the test passes and displayed if it fails.

    If you use a single string as a value of CPPUNIT_TEST_NAME, that has to be a class name. If that class has multiple tests, then all tests inside that class will be ran. The CPPUNIT_TEST_FIXTURE macro always creates a class with a single test function.

    You can discover the make targets by looking at the *.mk files in modules.