CANN/ge开发测试指南 GE DT Test Case Development Guide【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/geThis document establishes test case standards and basic requirements for UT and ST testing, addressing common issues encountered in DT testing. This document assumes readers have basic UT/ST development experience and can implement UT/ST test cases, so basic test case writing will not be covered in detail.This document only guides how to write UT/ST. Usege-dt-runnerskill to compile and run, or refer to docs/build.md for full test execution.Detailed GuidesDocumentContentApplicable ScenariosTest Framework GuideTest base classes, Graph DSL, faker, stub, checkerReference when constructing inputs, stubbing, and validating outputsUT Case Development GuideUT design checklist, validation methods, graph UT, rt2 kernel UTReference when writing UT casesST Case Development GuideST annotation requirements, scenario checklist, graph compilation/execution validation, DUMP phase listReference when writing ST casesFundamentalsWhen to Add DT Test CasesGenerally, DT test cases are added in two scenarios:During new requirement development, design and add UT/ST cases based on module designWhen fixing issues, including self-verified issues, issues found by pipelines during submission, and issues reported after submission. Theoretically, whenever a bug is discovered and youre fixing it, DT test cases should be added. Based on the tested behavior, you can choose to add UT cases or ST cases, or both. The recommended bug fix workflow is:DT Test EngineeringGE repository UT/ST code is located in the tests directory. The subdirectories under tests serve the following purposes:tests - acl_ut: UT case directory for ACL interfaces - autofuse: UT/ST case directory for automatic fusion - benchmark: Performance case directory - bin: Test executable artifacts and kernel binary directory - cmake: Common cmake scripts for test engineering - depends: Stub code for underlying dependencies and test helpers. Subdirectories are described below: - slog: Log interface stubs (slog, dlog, ascendalog) - runtime: Ascend runtime stubs (rtMalloc, rtMemcpy, rtLaunch, stream/event, etc.) - hccl: Collective communication stubs (HCCL AllReduce, etc.) - ascendcl: AscendCL API stubs (acl_rt, device management, memory, etc.) - op_stub: Operator implementation stubs, providing fake operator implementations for test graph executor scheduling - profiler: Profiling/Dump stubs - platform: Platform/chip information query stubs - error_manager: Error management stubs - graph_tuner: Graph tuning stubs - aoe: AOE (Ascend Optimization Engine) stubs - mmpa/mmpa2: Platform-independent memory/process abstraction API stubs - llm_datadist: LLM data distribution test helper stubs - helper_runtime: Helper runtime stubs (tsd_client, grpc_server, etc.) - aihacb_autofusion: Automatic fusion stubs - python: Python binding LLM wrapper module - checker: Header-only test validation tools (mem_trace_checker, shape_checker, etc.) - conf: Test configuration data (e.g., error_code.json) - symbol: Header-only symbolic shape inference test helper tools - dflow: UT/ST case directory for dflow module (including flow_graph, llm_datadist, pydflow, runner, udf submodules) - docs: Test-related documentation directory - engines: UT/ST case directories for various execution engines (including cpueng, dvppeng, ffts_engine, hccl_engine, nn_engine, rts_engine, te_fusion) - framework: Framework directory, containing common test code for UT/ST, such as faker, stub, easy_graph, etc. - ge: UT/ST case directory for GE core module (including ut, st subdirectories) - graph_metadef: UT/benchmark case directory for graph metadata definition module - parser: UT/ST case directory for model parsing module - python_tests: Python interface test directory - test_c: C interface test directoryBoth UT and ST use the googletest testing framework and provide AddressSanitizer for memory checking and gcov for coverage statistics. For new code, UT coverage should exceed 90%, and ST coverage should exceed 80%. For specific coverage statistics operations, refer to docs/build.md. The basic command is:# Run tests with -c parameter, automatically generate coverage statistics files to cov/ directory bash tests/run_test.sh -c [other parameters]Prerequisite: Need to installlcov(sudo apt-get install lcov) andpip3 install coverage, and gcc/gcov versions must be consistent in build and runtime environments.Adding New Test Case Files to Build SystemGE test engineering uses standard CMake build system without custom registration macros. After adding new case files, modify the CMakeLists.txt in the corresponding directory.Add source files to build variables# Method 1: Add to explicitly listed source file list set(MY_TEST_FILES graph/optimize/foo_unittest.cc ) # Method 2: When file(GLOB_RECURSE ...) exists, new files will be automatically includedTest Case StandardsStandard 1: UT Case File Name Should Be Tested_File_Name_unittest.ccFor example, if theres a classFoolocated infoo.cc, the corresponding UT case file name should be:foo_unittest.cc.If using googletests test suite test case approach, the test suite class name should beFooUT. Code example:// foo_unittest.cc #include gtest/gtest.h class FooUT : public testing::Test {}; // Test suite definition, class name is FooUT TEST_F(FooUT, case_name) { // Test case definition // ... }Using standardized naming makes it easier for subsequent developers to find corresponding files when reviewing cases, so please comply strictly.Standard 2: ST Case File Name Should Be Tested_Feature_systemtest.ccFor example, zero-copy ST cases can be named:zero_copy_systemtest.cc,zero_copy_system_test.ccis also acceptable. ST case granularity isfeature, do not use coverage to test amodulefor coverage purposes, such astensor_systemtest.cc.Standard 3: UT Case NamingUT case names use the following template:Tested_Interface_Expected_Behavior_Scenario, where each underscore-separated part uses PascalCase. For example, when testingFrameSelector, you want to test theSelectMainRootinterface, when the current graph is a subgraph of Main graph, the expected behavior is that after calling this interface, the created Node is still on the Main graph, the case name would be:SelectMainRoot_CreateOnMainRoot_CurrentFrameIsMainSubgraphs.Sometimes you only expect the interface to succeed, then the case name can be simplified toTested_Interface_ScenarioOk, for example, testing Tensor default constructor can successfully construct a Tensor, the case name can beDefaultConstructor_ConstructOk.Sometimes a module has many sub-functions, then the first field of the case name can also use the sub-function name:Sub_Function_Expected_Behavior_Scenario.Standard 4: One Test Case Should Only Validate One ScenarioWhether UT or ST cases, one case should only construct one scenario and validate it completely. If there are other scenarios, a new case should be written. The downside of this approach is some code redundancy and sacrifice of runtime efficiency, but the advantage is easier maintenance later.For example, the following case is a good example. This case constructs a Tensor through the constructor and completely validates whether the default values meet expectations:TEST_F(TensorUT, ConstructOk_V2) { TensorV2 tensor{{{8, 3, 224, 224}, {16, 3, 224, 224}}, // shape {ge::FORMAT_ND, ge::FORMAT_FRACTAL_NZ, {}}, // format kOnDeviceHbm, // placement ge::DT_FLOAT16, // dt nullptr}; const TensorV2 t2 tensor; EXPECT_EQ(t2.GetOriginShape(), Shape({8, 3, 224, 224})); EXPECT_EQ(t2.GetStorageShape(), Shape({16, 3, 224, 224})); EXPECT_EQ(t2.GetOriginFormat(), ge::FORMAT_ND); EXPECT_EQ(t2.GetStorageFormat(), ge::FORMAT_FRACTAL_NZ); StorageFormat storage_format{ge::FORMAT_ND, ge::FORMAT_FRACTAL_NZ, {}}; EXPECT_EQ(t2.GetFormat(), storage_format); EXPECT_EQ(t2.GetExpandDimsType(), ExpandDimsType{}); EXPECT_EQ(t2.GetPlacement(), kOnDeviceHbm); EXPECT_EQ(t2.GetDataType(), ge::DT_FLOAT16); EXPECT_EQ(t2.GetAddr(), nullptr); EXPECT_EQ(t2.GetDataint64_t(), nullptr); }Acceptable example: Although multiple sub-scenarios are validated in one case, theyre all validating null pointer scenarios;TEST_F(ModelOutpisUT, UpdateOutputShape_Failed_WhenNullptr) { StorageShape shape {{8, 3, 224, 224}, {8, 1, 224, 224, 16}}; kernel::BuildTensorAttr attr {kOnHost, ge::DT_FLOAT16, {ge::FORMAT_NCHW, ge::FORMAT_NC1HWC0, {}}}; auto tensor TensorFaker().Shape({}).Format(ge::FORMAT_ND).DataType(ge::DT_FLOAT).Build(); // First parameter is null auto context KernelRunContextFaker().Inputs({nullptr, tensor.GetTensor(), attr}).Build(); ASSERT_NE(kernel::UpdateOutputShape(context.GetContext()), ge::GRAPH_SUCCESS); // Second parameter is null context KernelRunContextFaker().Inputs({shape, nullptr, attr}).Build(); ASSERT_NE(kernel::UpdateOutputShape(context.GetContext()), ge::GRAPH_SUCCESS); // Third parameter is null context KernelRunContextFaker().Inputs({shape, tensor.GetTensor(), nullptr}).Build(); ASSERT_NE(kernel::UpdateOutputShape(context.GetContext()), ge::GRAPH_SUCCESS); }The following example is not acceptable:TEST_F(SinkNodeBinTest, test_sink_node_bin_with_handle_success) { kernel::BinData bin_data; const char *bin_key1 key1; const char *bin_key2 key2; const char *empty_key ; struct FakeRuntime : RuntimeStubImpl { rtError_t rtRegisterAllKernel(const rtDevBinary_t *bin, void **handle) { static size_t registed_num 0x10; *handle (void *)(registed_num); return RT_ERROR_NONE; } }; GertRuntimeStub runtime(std::unique_ptrRuntimeStubImpl(new FakeRuntime())); auto run_context BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)bin_data, nullptr); run_context.value_holder[1].Set((void *)bin_key1, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContextKernelContext()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValueuint64_t(), 0x10); run_context BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)bin_data, nullptr); run_context.value_holder[1].Set((void *)bin_key2, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContextKernelContext()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValueuint64_t(), 0x10 1); run_context BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)bin_data, nullptr); run_context.value_holder[1].Set((void *)bin_key1, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContextKernelContext()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValueuint64_t(), 0x10); // cached run_context BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)bin_data, nullptr); run_context.value_holder[1].Set((void *)empty_key, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContextKernelContext()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValueuint64_t(), 0x10 2); run_context BuildKernelRunContext(2, 1); run_context.value_holder[0].Set((void *)bin_data, nullptr); run_context.value_holder[1].Set((void *)empty_key, nullptr); ASSERT_EQ(kernel::SinkNodeBinWithHandle(run_context.GetContextKernelContext()), ge::GRAPH_SUCCESS); ASSERT_EQ(run_context.value_holder[2].GetValueuint64_t(), 0x10 3); }In the above example, it tests that SinkBin can succeed, tests that sinking the same bin multiple times does caching, and tests empty key scenarios. This causes the test cases test points to be scattered, unclear test intent, and difficult to name simply, leading to maintenance difficulties later.Standard 5: Do Not Define Private Functions as PublicWhether UT or ST, testing should be based on public interfaces. Even for UT, testing should be based on the modules public interfaces. Private functions and members are internal implementations of the module and should not be within the scope of testing or validation points. This is for case stability, as we believe a modules behavior is relatively stable, but the modules internal implementation can change arbitrarily.Standard 6: Do Not Enable Debug Level Logging or Graph Dump Switches in Test CasesDebug level logging and graph dump switches are generally tools for developer debugging. Enabling debug level logging or graph dump switches greatly affects code execution speed. Enabling these switches in a case affects this case and all subsequently executed cases, thereby causing the entire UT/ST project to run slowly. Therefore, do not enable these switches. If for debugging convenience, only enable during debugging and disable when submitting.Exceptions:When testing debug logging and graph dumps, switches can be enabled, but they need to be disabled when the case endsStandard 7: Test Cases Must Not Have Side EffectsA test cases execution should not affect other cases, nor should it affect itself. For example:If the case creates or modifies disk files, the corresponding files should be deleted or restored in TearDownIf the case adds or modifies environment variables, they should be restored in TearDownIf the case changes singleton or global variable states, they should be restored in TearDownStandard 8: Do Not Rely on External Initialization ActionsTest case initialization/deinitialization operations should be in the cases own setup/teardown functions. A case should not depend on external initialization. For example, some cases require certain .so files to be copied to a specific path before the case can execute correctly. If a case depends on certain .so files being in a specific path, the case should complete the .so copy in its own setup function and complete the .so deletion in teardown.【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考