warp-lang 1.0.1__py3-none-manylinux2014_x86_64.whl → 1.1.0__py3-none-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of warp-lang might be problematic. Click here for more details.
- warp/__init__.py +108 -97
- warp/__init__.pyi +1 -1
- warp/bin/warp-clang.so +0 -0
- warp/bin/warp.so +0 -0
- warp/build.py +115 -113
- warp/build_dll.py +383 -375
- warp/builtins.py +3425 -3354
- warp/codegen.py +2878 -2792
- warp/config.py +40 -36
- warp/constants.py +45 -45
- warp/context.py +5194 -5102
- warp/dlpack.py +442 -442
- warp/examples/__init__.py +16 -16
- warp/examples/assets/bear.usd +0 -0
- warp/examples/assets/bunny.usd +0 -0
- warp/examples/assets/cartpole.urdf +110 -110
- warp/examples/assets/crazyflie.usd +0 -0
- warp/examples/assets/cube.usd +0 -0
- warp/examples/assets/nv_ant.xml +92 -92
- warp/examples/assets/nv_humanoid.xml +183 -183
- warp/examples/assets/quadruped.urdf +267 -267
- warp/examples/assets/rocks.nvdb +0 -0
- warp/examples/assets/rocks.usd +0 -0
- warp/examples/assets/sphere.usd +0 -0
- warp/examples/benchmarks/benchmark_api.py +383 -383
- warp/examples/benchmarks/benchmark_cloth.py +278 -279
- warp/examples/benchmarks/benchmark_cloth_cupy.py +88 -88
- warp/examples/benchmarks/benchmark_cloth_jax.py +97 -100
- warp/examples/benchmarks/benchmark_cloth_numba.py +146 -142
- warp/examples/benchmarks/benchmark_cloth_numpy.py +77 -77
- warp/examples/benchmarks/benchmark_cloth_pytorch.py +86 -86
- warp/examples/benchmarks/benchmark_cloth_taichi.py +112 -112
- warp/examples/benchmarks/benchmark_cloth_warp.py +146 -146
- warp/examples/benchmarks/benchmark_launches.py +295 -295
- warp/examples/browse.py +29 -28
- warp/examples/core/example_dem.py +234 -221
- warp/examples/core/example_fluid.py +293 -267
- warp/examples/core/example_graph_capture.py +144 -129
- warp/examples/core/example_marching_cubes.py +188 -176
- warp/examples/core/example_mesh.py +174 -154
- warp/examples/core/example_mesh_intersect.py +205 -193
- warp/examples/core/example_nvdb.py +176 -169
- warp/examples/core/example_raycast.py +105 -89
- warp/examples/core/example_raymarch.py +199 -178
- warp/examples/core/example_render_opengl.py +185 -141
- warp/examples/core/example_sph.py +405 -389
- warp/examples/core/example_torch.py +222 -181
- warp/examples/core/example_wave.py +263 -249
- warp/examples/fem/bsr_utils.py +378 -380
- warp/examples/fem/example_apic_fluid.py +407 -391
- warp/examples/fem/example_convection_diffusion.py +182 -168
- warp/examples/fem/example_convection_diffusion_dg.py +219 -209
- warp/examples/fem/example_convection_diffusion_dg0.py +204 -194
- warp/examples/fem/example_deformed_geometry.py +177 -159
- warp/examples/fem/example_diffusion.py +201 -173
- warp/examples/fem/example_diffusion_3d.py +177 -152
- warp/examples/fem/example_diffusion_mgpu.py +221 -214
- warp/examples/fem/example_mixed_elasticity.py +244 -222
- warp/examples/fem/example_navier_stokes.py +259 -243
- warp/examples/fem/example_stokes.py +220 -192
- warp/examples/fem/example_stokes_transfer.py +265 -249
- warp/examples/fem/mesh_utils.py +133 -109
- warp/examples/fem/plot_utils.py +292 -287
- warp/examples/optim/example_bounce.py +260 -248
- warp/examples/optim/example_cloth_throw.py +222 -210
- warp/examples/optim/example_diffray.py +566 -535
- warp/examples/optim/example_drone.py +864 -835
- warp/examples/optim/example_inverse_kinematics.py +176 -169
- warp/examples/optim/example_inverse_kinematics_torch.py +185 -170
- warp/examples/optim/example_spring_cage.py +239 -234
- warp/examples/optim/example_trajectory.py +223 -201
- warp/examples/optim/example_walker.py +306 -292
- warp/examples/sim/example_cartpole.py +139 -128
- warp/examples/sim/example_cloth.py +196 -184
- warp/examples/sim/example_granular.py +124 -113
- warp/examples/sim/example_granular_collision_sdf.py +197 -185
- warp/examples/sim/example_jacobian_ik.py +236 -213
- warp/examples/sim/example_particle_chain.py +118 -106
- warp/examples/sim/example_quadruped.py +193 -179
- warp/examples/sim/example_rigid_chain.py +197 -189
- warp/examples/sim/example_rigid_contact.py +189 -176
- warp/examples/sim/example_rigid_force.py +127 -126
- warp/examples/sim/example_rigid_gyroscopic.py +109 -97
- warp/examples/sim/example_rigid_soft_contact.py +134 -124
- warp/examples/sim/example_soft_body.py +190 -178
- warp/fabric.py +337 -335
- warp/fem/__init__.py +60 -27
- warp/fem/cache.py +401 -388
- warp/fem/dirichlet.py +178 -179
- warp/fem/domain.py +262 -263
- warp/fem/field/__init__.py +100 -101
- warp/fem/field/field.py +148 -149
- warp/fem/field/nodal_field.py +298 -299
- warp/fem/field/restriction.py +22 -21
- warp/fem/field/test.py +180 -181
- warp/fem/field/trial.py +183 -183
- warp/fem/geometry/__init__.py +15 -19
- warp/fem/geometry/closest_point.py +69 -70
- warp/fem/geometry/deformed_geometry.py +270 -271
- warp/fem/geometry/element.py +744 -744
- warp/fem/geometry/geometry.py +184 -186
- warp/fem/geometry/grid_2d.py +380 -373
- warp/fem/geometry/grid_3d.py +441 -435
- warp/fem/geometry/hexmesh.py +953 -953
- warp/fem/geometry/partition.py +374 -376
- warp/fem/geometry/quadmesh_2d.py +532 -532
- warp/fem/geometry/tetmesh.py +840 -840
- warp/fem/geometry/trimesh_2d.py +577 -577
- warp/fem/integrate.py +1630 -1615
- warp/fem/operator.py +190 -191
- warp/fem/polynomial.py +214 -213
- warp/fem/quadrature/__init__.py +2 -2
- warp/fem/quadrature/pic_quadrature.py +243 -245
- warp/fem/quadrature/quadrature.py +295 -294
- warp/fem/space/__init__.py +294 -292
- warp/fem/space/basis_space.py +488 -489
- warp/fem/space/collocated_function_space.py +100 -105
- warp/fem/space/dof_mapper.py +236 -236
- warp/fem/space/function_space.py +148 -145
- warp/fem/space/grid_2d_function_space.py +267 -267
- warp/fem/space/grid_3d_function_space.py +305 -306
- warp/fem/space/hexmesh_function_space.py +350 -352
- warp/fem/space/partition.py +350 -350
- warp/fem/space/quadmesh_2d_function_space.py +368 -369
- warp/fem/space/restriction.py +158 -160
- warp/fem/space/shape/__init__.py +13 -15
- warp/fem/space/shape/cube_shape_function.py +738 -738
- warp/fem/space/shape/shape_function.py +102 -103
- warp/fem/space/shape/square_shape_function.py +611 -611
- warp/fem/space/shape/tet_shape_function.py +565 -567
- warp/fem/space/shape/triangle_shape_function.py +429 -429
- warp/fem/space/tetmesh_function_space.py +294 -292
- warp/fem/space/topology.py +297 -295
- warp/fem/space/trimesh_2d_function_space.py +223 -221
- warp/fem/types.py +77 -77
- warp/fem/utils.py +495 -495
- warp/jax.py +166 -141
- warp/jax_experimental.py +341 -339
- warp/native/array.h +1072 -1025
- warp/native/builtin.h +1560 -1560
- warp/native/bvh.cpp +398 -398
- warp/native/bvh.cu +525 -525
- warp/native/bvh.h +429 -429
- warp/native/clang/clang.cpp +495 -464
- warp/native/crt.cpp +31 -31
- warp/native/crt.h +334 -334
- warp/native/cuda_crt.h +1049 -1049
- warp/native/cuda_util.cpp +549 -540
- warp/native/cuda_util.h +288 -203
- warp/native/cutlass_gemm.cpp +34 -34
- warp/native/cutlass_gemm.cu +372 -372
- warp/native/error.cpp +66 -66
- warp/native/error.h +27 -27
- warp/native/fabric.h +228 -228
- warp/native/hashgrid.cpp +301 -278
- warp/native/hashgrid.cu +78 -77
- warp/native/hashgrid.h +227 -227
- warp/native/initializer_array.h +32 -32
- warp/native/intersect.h +1204 -1204
- warp/native/intersect_adj.h +365 -365
- warp/native/intersect_tri.h +322 -322
- warp/native/marching.cpp +2 -2
- warp/native/marching.cu +497 -497
- warp/native/marching.h +2 -2
- warp/native/mat.h +1498 -1498
- warp/native/matnn.h +333 -333
- warp/native/mesh.cpp +203 -203
- warp/native/mesh.cu +293 -293
- warp/native/mesh.h +1887 -1887
- warp/native/nanovdb/NanoVDB.h +4782 -4782
- warp/native/nanovdb/PNanoVDB.h +2553 -2553
- warp/native/nanovdb/PNanoVDBWrite.h +294 -294
- warp/native/noise.h +850 -850
- warp/native/quat.h +1084 -1084
- warp/native/rand.h +299 -299
- warp/native/range.h +108 -108
- warp/native/reduce.cpp +156 -156
- warp/native/reduce.cu +348 -348
- warp/native/runlength_encode.cpp +61 -61
- warp/native/runlength_encode.cu +46 -46
- warp/native/scan.cpp +30 -30
- warp/native/scan.cu +36 -36
- warp/native/scan.h +7 -7
- warp/native/solid_angle.h +442 -442
- warp/native/sort.cpp +94 -94
- warp/native/sort.cu +97 -97
- warp/native/sort.h +14 -14
- warp/native/sparse.cpp +337 -337
- warp/native/sparse.cu +544 -544
- warp/native/spatial.h +630 -630
- warp/native/svd.h +562 -562
- warp/native/temp_buffer.h +30 -30
- warp/native/vec.h +1132 -1132
- warp/native/volume.cpp +297 -297
- warp/native/volume.cu +32 -32
- warp/native/volume.h +538 -538
- warp/native/volume_builder.cu +425 -425
- warp/native/volume_builder.h +19 -19
- warp/native/warp.cpp +1057 -1052
- warp/native/warp.cu +2943 -2828
- warp/native/warp.h +313 -305
- warp/optim/__init__.py +9 -9
- warp/optim/adam.py +120 -120
- warp/optim/linear.py +1104 -939
- warp/optim/sgd.py +104 -92
- warp/render/__init__.py +10 -10
- warp/render/render_opengl.py +3217 -3204
- warp/render/render_usd.py +768 -749
- warp/render/utils.py +152 -150
- warp/sim/__init__.py +52 -59
- warp/sim/articulation.py +685 -685
- warp/sim/collide.py +1594 -1590
- warp/sim/import_mjcf.py +489 -481
- warp/sim/import_snu.py +220 -221
- warp/sim/import_urdf.py +536 -516
- warp/sim/import_usd.py +887 -881
- warp/sim/inertia.py +316 -317
- warp/sim/integrator.py +234 -233
- warp/sim/integrator_euler.py +1956 -1956
- warp/sim/integrator_featherstone.py +1910 -1991
- warp/sim/integrator_xpbd.py +3294 -3312
- warp/sim/model.py +4473 -4314
- warp/sim/particles.py +113 -112
- warp/sim/render.py +417 -403
- warp/sim/utils.py +413 -410
- warp/sparse.py +1227 -1227
- warp/stubs.py +2109 -2469
- warp/tape.py +1162 -225
- warp/tests/__init__.py +1 -1
- warp/tests/__main__.py +4 -4
- warp/tests/assets/torus.usda +105 -105
- warp/tests/aux_test_class_kernel.py +26 -26
- warp/tests/aux_test_compile_consts_dummy.py +10 -10
- warp/tests/aux_test_conditional_unequal_types_kernels.py +21 -21
- warp/tests/aux_test_dependent.py +22 -22
- warp/tests/aux_test_grad_customs.py +23 -23
- warp/tests/aux_test_reference.py +11 -11
- warp/tests/aux_test_reference_reference.py +10 -10
- warp/tests/aux_test_square.py +17 -17
- warp/tests/aux_test_unresolved_func.py +14 -14
- warp/tests/aux_test_unresolved_symbol.py +14 -14
- warp/tests/disabled_kinematics.py +239 -239
- warp/tests/run_coverage_serial.py +31 -31
- warp/tests/test_adam.py +157 -157
- warp/tests/test_arithmetic.py +1124 -1124
- warp/tests/test_array.py +2417 -2326
- warp/tests/test_array_reduce.py +150 -150
- warp/tests/test_async.py +668 -656
- warp/tests/test_atomic.py +141 -141
- warp/tests/test_bool.py +204 -149
- warp/tests/test_builtins_resolution.py +1292 -1292
- warp/tests/test_bvh.py +164 -171
- warp/tests/test_closest_point_edge_edge.py +228 -228
- warp/tests/test_codegen.py +566 -553
- warp/tests/test_compile_consts.py +97 -101
- warp/tests/test_conditional.py +246 -246
- warp/tests/test_copy.py +232 -215
- warp/tests/test_ctypes.py +632 -632
- warp/tests/test_dense.py +67 -67
- warp/tests/test_devices.py +91 -98
- warp/tests/test_dlpack.py +530 -529
- warp/tests/test_examples.py +400 -378
- warp/tests/test_fabricarray.py +955 -955
- warp/tests/test_fast_math.py +62 -54
- warp/tests/test_fem.py +1277 -1278
- warp/tests/test_fp16.py +130 -130
- warp/tests/test_func.py +338 -337
- warp/tests/test_generics.py +571 -571
- warp/tests/test_grad.py +746 -640
- warp/tests/test_grad_customs.py +333 -336
- warp/tests/test_hash_grid.py +210 -164
- warp/tests/test_import.py +39 -39
- warp/tests/test_indexedarray.py +1134 -1134
- warp/tests/test_intersect.py +67 -67
- warp/tests/test_jax.py +307 -307
- warp/tests/test_large.py +167 -164
- warp/tests/test_launch.py +354 -354
- warp/tests/test_lerp.py +261 -261
- warp/tests/test_linear_solvers.py +191 -171
- warp/tests/test_lvalue.py +421 -493
- warp/tests/test_marching_cubes.py +65 -65
- warp/tests/test_mat.py +1801 -1827
- warp/tests/test_mat_lite.py +115 -115
- warp/tests/test_mat_scalar_ops.py +2907 -2889
- warp/tests/test_math.py +126 -193
- warp/tests/test_matmul.py +500 -499
- warp/tests/test_matmul_lite.py +410 -410
- warp/tests/test_mempool.py +188 -190
- warp/tests/test_mesh.py +284 -324
- warp/tests/test_mesh_query_aabb.py +228 -241
- warp/tests/test_mesh_query_point.py +692 -702
- warp/tests/test_mesh_query_ray.py +292 -303
- warp/tests/test_mlp.py +276 -276
- warp/tests/test_model.py +110 -110
- warp/tests/test_modules_lite.py +39 -39
- warp/tests/test_multigpu.py +163 -163
- warp/tests/test_noise.py +248 -248
- warp/tests/test_operators.py +250 -250
- warp/tests/test_options.py +123 -125
- warp/tests/test_peer.py +133 -137
- warp/tests/test_pinned.py +78 -78
- warp/tests/test_print.py +54 -54
- warp/tests/test_quat.py +2086 -2086
- warp/tests/test_rand.py +288 -288
- warp/tests/test_reload.py +217 -217
- warp/tests/test_rounding.py +179 -179
- warp/tests/test_runlength_encode.py +190 -190
- warp/tests/test_sim_grad.py +243 -0
- warp/tests/test_sim_kinematics.py +91 -97
- warp/tests/test_smoothstep.py +168 -168
- warp/tests/test_snippet.py +305 -266
- warp/tests/test_sparse.py +468 -460
- warp/tests/test_spatial.py +2148 -2148
- warp/tests/test_streams.py +486 -473
- warp/tests/test_struct.py +710 -675
- warp/tests/test_tape.py +173 -148
- warp/tests/test_torch.py +743 -743
- warp/tests/test_transient_module.py +87 -87
- warp/tests/test_types.py +556 -659
- warp/tests/test_utils.py +490 -499
- warp/tests/test_vec.py +1264 -1268
- warp/tests/test_vec_lite.py +73 -73
- warp/tests/test_vec_scalar_ops.py +2099 -2099
- warp/tests/test_verify_fp.py +94 -94
- warp/tests/test_volume.py +737 -736
- warp/tests/test_volume_write.py +255 -265
- warp/tests/unittest_serial.py +37 -37
- warp/tests/unittest_suites.py +363 -359
- warp/tests/unittest_utils.py +603 -578
- warp/tests/unused_test_misc.py +71 -71
- warp/tests/walkthrough_debug.py +85 -85
- warp/thirdparty/appdirs.py +598 -598
- warp/thirdparty/dlpack.py +143 -143
- warp/thirdparty/unittest_parallel.py +566 -561
- warp/torch.py +321 -295
- warp/types.py +4504 -4450
- warp/utils.py +1008 -821
- {warp_lang-1.0.1.dist-info → warp_lang-1.1.0.dist-info}/LICENSE.md +126 -126
- {warp_lang-1.0.1.dist-info → warp_lang-1.1.0.dist-info}/METADATA +338 -400
- warp_lang-1.1.0.dist-info/RECORD +352 -0
- warp/examples/assets/cube.usda +0 -42
- warp/examples/assets/sphere.usda +0 -56
- warp/examples/assets/torus.usda +0 -105
- warp_lang-1.0.1.dist-info/RECORD +0 -352
- {warp_lang-1.0.1.dist-info → warp_lang-1.1.0.dist-info}/WHEEL +0 -0
- {warp_lang-1.0.1.dist-info → warp_lang-1.1.0.dist-info}/top_level.txt +0 -0
warp/tests/unittest_utils.py
CHANGED
|
@@ -1,578 +1,603 @@
|
|
|
1
|
-
# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved.
|
|
2
|
-
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
|
3
|
-
# and proprietary rights in and to this software, related documentation
|
|
4
|
-
# and any modifications thereto. Any use, reproduction, disclosure or
|
|
5
|
-
# distribution of this software and related documentation without an express
|
|
6
|
-
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
|
7
|
-
|
|
8
|
-
import ctypes
|
|
9
|
-
import ctypes.util
|
|
10
|
-
import
|
|
11
|
-
import os
|
|
12
|
-
import sys
|
|
13
|
-
import time
|
|
14
|
-
import unittest
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
#
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
#
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
def
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
def
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
#
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
def
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
self.
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
self.
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
self.
|
|
463
|
-
|
|
464
|
-
def
|
|
465
|
-
super(unittest.TextTestResult, self).
|
|
466
|
-
self._add_helper(test, "
|
|
467
|
-
self._record_test(test, "
|
|
468
|
-
|
|
469
|
-
def
|
|
470
|
-
super(unittest.TextTestResult, self).
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
self.
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
self.
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
self.
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
super(unittest.TextTestResult, self).
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
self.
|
|
535
|
-
|
|
536
|
-
def
|
|
537
|
-
super(unittest.TextTestResult, self).
|
|
538
|
-
self._add_helper(test, "
|
|
539
|
-
self.
|
|
540
|
-
|
|
541
|
-
def
|
|
542
|
-
super(unittest.TextTestResult, self).
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
self.
|
|
553
|
-
self.
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
self.
|
|
559
|
-
self.
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
self.
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
)
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
)
|
|
577
|
-
self.stream.writeln(f"##teamcity[
|
|
578
|
-
self.stream.flush()
|
|
1
|
+
# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved.
|
|
2
|
+
# NVIDIA CORPORATION and its licensors retain all intellectual property
|
|
3
|
+
# and proprietary rights in and to this software, related documentation
|
|
4
|
+
# and any modifications thereto. Any use, reproduction, disclosure or
|
|
5
|
+
# distribution of this software and related documentation without an express
|
|
6
|
+
# license agreement from NVIDIA CORPORATION is strictly prohibited.
|
|
7
|
+
|
|
8
|
+
import ctypes
|
|
9
|
+
import ctypes.util
|
|
10
|
+
import importlib
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
import time
|
|
14
|
+
import unittest
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
19
|
+
import warp as wp
|
|
20
|
+
|
|
21
|
+
pxr = importlib.util.find_spec("pxr")
|
|
22
|
+
USD_AVAILABLE = pxr is not None
|
|
23
|
+
|
|
24
|
+
# default test mode (see get_test_devices())
|
|
25
|
+
# "basic" - only run on CPU and first GPU device
|
|
26
|
+
# "unique" - run on CPU and all unique GPU arches
|
|
27
|
+
# "unique_or_2x" - run on CPU and all unique GPU arches. If there is a single GPU arch, add a second GPU if it exists.
|
|
28
|
+
# "all" - run on all devices
|
|
29
|
+
test_mode = "unique_or_2x"
|
|
30
|
+
|
|
31
|
+
coverage_enabled = False
|
|
32
|
+
coverage_temp_dir = None
|
|
33
|
+
coverage_branch = None
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
if sys.platform == "win32":
|
|
37
|
+
LIBC = ctypes.CDLL("ucrtbase.dll")
|
|
38
|
+
else:
|
|
39
|
+
LIBC = ctypes.CDLL(ctypes.util.find_library("c"))
|
|
40
|
+
except OSError:
|
|
41
|
+
print("Failed to load the standard C library")
|
|
42
|
+
LIBC = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_selected_cuda_test_devices(mode: Optional[str] = None):
|
|
46
|
+
"""Returns a list of CUDA devices according the selected ``mode`` behavior.
|
|
47
|
+
|
|
48
|
+
If ``mode`` is ``None``, the ``global test_mode`` value will be used and
|
|
49
|
+
this list will be a subset of the devices returned from ``get_test_devices()``.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
mode: ``"basic"``, returns a list containing up to a single CUDA device.
|
|
53
|
+
``"unique"``, returns a list containing no more than one device of
|
|
54
|
+
every CUDA architecture on the system.
|
|
55
|
+
``"unique_or_2x"`` behaves like ``"unique"`` but adds up to one
|
|
56
|
+
additional CUDA device if the system only devices of a single CUDA
|
|
57
|
+
architecture.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
if mode is None:
|
|
61
|
+
global test_mode
|
|
62
|
+
mode = test_mode
|
|
63
|
+
|
|
64
|
+
if mode == "basic":
|
|
65
|
+
if wp.is_cuda_available():
|
|
66
|
+
return [wp.get_device("cuda:0")]
|
|
67
|
+
else:
|
|
68
|
+
return []
|
|
69
|
+
|
|
70
|
+
cuda_devices = wp.get_cuda_devices()
|
|
71
|
+
first_cuda_devices = {}
|
|
72
|
+
|
|
73
|
+
for d in cuda_devices:
|
|
74
|
+
if d.arch not in first_cuda_devices:
|
|
75
|
+
first_cuda_devices[d.arch] = d
|
|
76
|
+
|
|
77
|
+
selected_cuda_devices = list(first_cuda_devices.values())
|
|
78
|
+
|
|
79
|
+
if mode == "unique_or_2x" and len(selected_cuda_devices) == 1 and len(cuda_devices) > 1:
|
|
80
|
+
for d in cuda_devices:
|
|
81
|
+
if d not in selected_cuda_devices:
|
|
82
|
+
selected_cuda_devices.append(d)
|
|
83
|
+
break
|
|
84
|
+
|
|
85
|
+
return selected_cuda_devices
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_test_devices(mode: Optional[str] = None):
|
|
89
|
+
"""Returns a list of devices based on the mode selected.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
mode: The testing mode to specify which devices to include. If not provided or ``None``, the
|
|
93
|
+
``global test_mode`` value will be used.
|
|
94
|
+
"basic": Returns the CPU and the first GPU device when available.
|
|
95
|
+
"unique": Returns the CPU and all unique GPU architectures.
|
|
96
|
+
"unique_or_2x" (default): Behaves like "unique" but adds up to one additional CUDA device
|
|
97
|
+
if the system only devices of a single CUDA architecture.
|
|
98
|
+
"all": Returns all available devices.
|
|
99
|
+
"""
|
|
100
|
+
if mode is None:
|
|
101
|
+
global test_mode
|
|
102
|
+
mode = test_mode
|
|
103
|
+
|
|
104
|
+
devices = []
|
|
105
|
+
|
|
106
|
+
if mode == "basic":
|
|
107
|
+
# only run on CPU and first GPU device
|
|
108
|
+
if wp.is_cpu_available():
|
|
109
|
+
devices.append(wp.get_device("cpu"))
|
|
110
|
+
if wp.is_cuda_available():
|
|
111
|
+
devices.append(wp.get_device("cuda:0"))
|
|
112
|
+
elif mode == "unique" or mode == "unique_or_2x":
|
|
113
|
+
# run on CPU and a subset of GPUs
|
|
114
|
+
if wp.is_cpu_available():
|
|
115
|
+
devices.append(wp.get_device("cpu"))
|
|
116
|
+
devices.extend(get_selected_cuda_test_devices(mode))
|
|
117
|
+
elif mode == "all":
|
|
118
|
+
# run on all devices
|
|
119
|
+
devices = wp.get_devices()
|
|
120
|
+
else:
|
|
121
|
+
raise ValueError(f"Unknown test mode selected: {mode}")
|
|
122
|
+
|
|
123
|
+
return devices
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def get_cuda_test_devices(mode=None):
|
|
127
|
+
devices = get_test_devices(mode=mode)
|
|
128
|
+
return [d for d in devices if d.is_cuda]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# redirects and captures all stdout output (including from C-libs)
|
|
132
|
+
class StdOutCapture:
|
|
133
|
+
def begin(self):
|
|
134
|
+
# Flush the stream buffers managed by libc.
|
|
135
|
+
# This is needed at the moment due to Carbonite not flushing the logs
|
|
136
|
+
# being printed out when extensions are starting up.
|
|
137
|
+
if LIBC is not None:
|
|
138
|
+
LIBC.fflush(None)
|
|
139
|
+
|
|
140
|
+
# save original
|
|
141
|
+
self.saved = sys.stdout
|
|
142
|
+
self.target = os.dup(self.saved.fileno())
|
|
143
|
+
|
|
144
|
+
# create temporary capture stream
|
|
145
|
+
import io
|
|
146
|
+
import tempfile
|
|
147
|
+
|
|
148
|
+
self.tempfile = io.TextIOWrapper(
|
|
149
|
+
tempfile.TemporaryFile(buffering=0),
|
|
150
|
+
encoding="utf-8",
|
|
151
|
+
errors="replace",
|
|
152
|
+
newline="",
|
|
153
|
+
write_through=True,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
os.dup2(self.tempfile.fileno(), self.saved.fileno())
|
|
157
|
+
|
|
158
|
+
sys.stdout = self.tempfile
|
|
159
|
+
|
|
160
|
+
def end(self):
|
|
161
|
+
# The following sleep doesn't seem to fix the test_print failure on Windows
|
|
162
|
+
# if sys.platform == "win32":
|
|
163
|
+
# # Workaround for what seems to be a Windows-specific bug where
|
|
164
|
+
# # the output of CUDA's `printf` is not being immediately flushed
|
|
165
|
+
# # despite the context synchronisation.
|
|
166
|
+
# time.sleep(0.01)
|
|
167
|
+
|
|
168
|
+
if LIBC is not None:
|
|
169
|
+
LIBC.fflush(None)
|
|
170
|
+
|
|
171
|
+
os.dup2(self.target, self.saved.fileno())
|
|
172
|
+
os.close(self.target)
|
|
173
|
+
|
|
174
|
+
self.tempfile.seek(0)
|
|
175
|
+
res = self.tempfile.buffer.read()
|
|
176
|
+
self.tempfile.close()
|
|
177
|
+
|
|
178
|
+
sys.stdout = self.saved
|
|
179
|
+
|
|
180
|
+
return str(res.decode("utf-8"))
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class CheckOutput:
|
|
184
|
+
def __init__(self, test):
|
|
185
|
+
self.test = test
|
|
186
|
+
|
|
187
|
+
def __enter__(self):
|
|
188
|
+
# wp.force_load()
|
|
189
|
+
|
|
190
|
+
self.capture = StdOutCapture()
|
|
191
|
+
self.capture.begin()
|
|
192
|
+
|
|
193
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
194
|
+
# ensure any stdout output is flushed
|
|
195
|
+
wp.synchronize()
|
|
196
|
+
|
|
197
|
+
s = self.capture.end()
|
|
198
|
+
if s != "":
|
|
199
|
+
print(s.rstrip())
|
|
200
|
+
|
|
201
|
+
# fail if test produces unexpected output (e.g.: from wp.expect_eq() builtins)
|
|
202
|
+
# we allow strings starting of the form "Module xxx load on device xxx"
|
|
203
|
+
# for lazy loaded modules
|
|
204
|
+
if s != "" and not s.startswith("Module"):
|
|
205
|
+
self.test.fail(f"Unexpected output:\n'{s.rstrip()}'")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def assert_array_equal(result: wp.array, expect: wp.array):
|
|
209
|
+
np.testing.assert_equal(result.numpy(), expect.numpy())
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def assert_np_equal(result: np.ndarray, expect: np.ndarray, tol=0.0):
|
|
213
|
+
if tol != 0.0:
|
|
214
|
+
# TODO: Get all tests working without the .flatten()
|
|
215
|
+
np.testing.assert_allclose(result.flatten(), expect.flatten(), atol=tol, equal_nan=True)
|
|
216
|
+
else:
|
|
217
|
+
# TODO: Get all tests working with strict=True
|
|
218
|
+
np.testing.assert_array_equal(result, expect)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
# if check_output is True any output to stdout will be treated as an error
|
|
222
|
+
def create_test_func(func, device, check_output, **kwargs):
|
|
223
|
+
# pass args to func
|
|
224
|
+
def test_func(self):
|
|
225
|
+
if check_output:
|
|
226
|
+
with CheckOutput(self):
|
|
227
|
+
func(self, device, **kwargs)
|
|
228
|
+
else:
|
|
229
|
+
func(self, device, **kwargs)
|
|
230
|
+
|
|
231
|
+
return test_func
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def skip_test_func(self):
|
|
235
|
+
# A function to use so we can tell unittest that the test was skipped.
|
|
236
|
+
self.skipTest("No suitable devices to run the test.")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def sanitize_identifier(s):
|
|
240
|
+
"""replace all non-identifier characters with '_'"""
|
|
241
|
+
|
|
242
|
+
s = str(s)
|
|
243
|
+
if s.isidentifier():
|
|
244
|
+
return s
|
|
245
|
+
else:
|
|
246
|
+
import re
|
|
247
|
+
|
|
248
|
+
return re.sub(r"\W|^(?=\d)", "_", s)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def add_function_test(cls, name, func, devices=None, check_output=True, **kwargs):
|
|
252
|
+
if devices is None:
|
|
253
|
+
setattr(cls, name, create_test_func(func, None, check_output, **kwargs))
|
|
254
|
+
elif isinstance(devices, list):
|
|
255
|
+
if not devices:
|
|
256
|
+
# No devices to run this test
|
|
257
|
+
setattr(cls, name, skip_test_func)
|
|
258
|
+
else:
|
|
259
|
+
for device in devices:
|
|
260
|
+
setattr(
|
|
261
|
+
cls,
|
|
262
|
+
name + "_" + sanitize_identifier(device),
|
|
263
|
+
create_test_func(func, device, check_output, **kwargs),
|
|
264
|
+
)
|
|
265
|
+
else:
|
|
266
|
+
setattr(
|
|
267
|
+
cls,
|
|
268
|
+
name + "_" + sanitize_identifier(devices),
|
|
269
|
+
create_test_func(func, devices, check_output, **kwargs),
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def add_kernel_test(cls, kernel, dim, name=None, expect=None, inputs=None, devices=None):
|
|
274
|
+
def test_func(self, device):
|
|
275
|
+
args = []
|
|
276
|
+
if inputs:
|
|
277
|
+
args.extend(inputs)
|
|
278
|
+
|
|
279
|
+
if expect:
|
|
280
|
+
# allocate outputs to match results
|
|
281
|
+
result = wp.array(expect, dtype=int, device=device)
|
|
282
|
+
output = wp.zeros_like(result)
|
|
283
|
+
|
|
284
|
+
args.append(output)
|
|
285
|
+
|
|
286
|
+
# force load so that we don't generate any log output during launch
|
|
287
|
+
kernel.module.load(device)
|
|
288
|
+
|
|
289
|
+
with CheckOutput(self):
|
|
290
|
+
wp.launch(kernel, dim=dim, inputs=args, device=device)
|
|
291
|
+
|
|
292
|
+
# check output values
|
|
293
|
+
if expect:
|
|
294
|
+
assert_array_equal(output, result)
|
|
295
|
+
|
|
296
|
+
if name is None:
|
|
297
|
+
name = kernel.key
|
|
298
|
+
|
|
299
|
+
# device is required for kernel tests, so use all devices if none were given
|
|
300
|
+
if devices is None:
|
|
301
|
+
devices = get_test_devices()
|
|
302
|
+
|
|
303
|
+
# register test func with class for the given devices
|
|
304
|
+
for d in devices:
|
|
305
|
+
# use a function to forward the device to the inner test function
|
|
306
|
+
def test_func_wrapper(test, device=d):
|
|
307
|
+
test_func(test, device)
|
|
308
|
+
|
|
309
|
+
setattr(cls, name + "_" + sanitize_identifier(d), test_func_wrapper)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# helper that first calls the test function to generate all kernel permutations
|
|
313
|
+
# so that compilation is done in one-shot instead of per-test
|
|
314
|
+
def add_function_test_register_kernel(cls, name, func, devices=None, **kwargs):
|
|
315
|
+
func(None, None, **kwargs, register_kernels=True)
|
|
316
|
+
add_function_test(cls, name, func, devices=devices, **kwargs)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class TeamCityTestResult(unittest.TextTestResult):
|
|
320
|
+
"""This class will report each test result to TeamCity"""
|
|
321
|
+
|
|
322
|
+
def __init__(self, stream, descriptions, verbosity):
|
|
323
|
+
super(TeamCityTestResult, self).__init__(stream, descriptions, verbosity)
|
|
324
|
+
|
|
325
|
+
def addSuccess(self, test):
|
|
326
|
+
super(TeamCityTestResult, self).addSuccess(test)
|
|
327
|
+
self.reportSuccess(test)
|
|
328
|
+
|
|
329
|
+
def addError(self, test, err):
|
|
330
|
+
super(TeamCityTestResult, self).addError(test, err)
|
|
331
|
+
self.reportFailure(test)
|
|
332
|
+
|
|
333
|
+
def addFailure(self, test, err):
|
|
334
|
+
super(TeamCityTestResult, self).addFailure(test, err)
|
|
335
|
+
self.reportFailure(test)
|
|
336
|
+
|
|
337
|
+
def addSkip(self, test, reason):
|
|
338
|
+
super(TeamCityTestResult, self).addSkip(test, reason)
|
|
339
|
+
|
|
340
|
+
def addExpectedFailure(self, test, err):
|
|
341
|
+
super(TeamCityTestResult, self).addExpectedFailure(test, err)
|
|
342
|
+
self.reportSuccess(test)
|
|
343
|
+
|
|
344
|
+
def addUnexpectedSuccess(self, test):
|
|
345
|
+
super(TeamCityTestResult, self).addUnexpectedSuccess(test)
|
|
346
|
+
self.reportFailure(test)
|
|
347
|
+
|
|
348
|
+
def reportSuccess(self, test):
|
|
349
|
+
test_id = test.id()
|
|
350
|
+
print(f"##teamcity[testStarted name='{test_id}']")
|
|
351
|
+
print(f"##teamcity[testFinished name='{test_id}']")
|
|
352
|
+
|
|
353
|
+
def reportFailure(self, test):
|
|
354
|
+
test_id = test.id()
|
|
355
|
+
print(f"##teamcity[testStarted name='{test_id}']")
|
|
356
|
+
print(f"##teamcity[testFailed name='{test_id}']")
|
|
357
|
+
print(f"##teamcity[testFinished name='{test_id}']")
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class TeamCityTestRunner(unittest.TextTestRunner):
|
|
361
|
+
"""Test runner that will report test results to TeamCity if running in TeamCity"""
|
|
362
|
+
|
|
363
|
+
def __init__(self, **kwargs):
|
|
364
|
+
self.running_in_teamcity = os.environ.get("TEAMCITY_VERSION") is not None
|
|
365
|
+
if self.running_in_teamcity:
|
|
366
|
+
kwargs["resultclass"] = TeamCityTestResult
|
|
367
|
+
super(TeamCityTestRunner, self).__init__(**kwargs)
|
|
368
|
+
|
|
369
|
+
def run(self, test, name):
|
|
370
|
+
if self.running_in_teamcity:
|
|
371
|
+
print(f"##teamcity[testSuiteStarted name='{name}']")
|
|
372
|
+
|
|
373
|
+
result = super(TeamCityTestRunner, self).run(test)
|
|
374
|
+
|
|
375
|
+
if self.running_in_teamcity:
|
|
376
|
+
print(f"##teamcity[testSuiteFinished name='{name}']")
|
|
377
|
+
if not result.wasSuccessful():
|
|
378
|
+
print("##teamcity[buildStatus status='FAILURE']")
|
|
379
|
+
|
|
380
|
+
return result
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def write_junit_results(
|
|
384
|
+
outfile: str,
|
|
385
|
+
test_records: list,
|
|
386
|
+
tests_run: int,
|
|
387
|
+
tests_failed: int,
|
|
388
|
+
tests_errored: int,
|
|
389
|
+
tests_skipped: int,
|
|
390
|
+
test_duration: float,
|
|
391
|
+
):
|
|
392
|
+
"""Write a JUnit XML from our report data
|
|
393
|
+
|
|
394
|
+
The report file is needed for GitLab to add test reports in merge requests.
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
import xml.etree.ElementTree as ET
|
|
398
|
+
|
|
399
|
+
root = ET.Element(
|
|
400
|
+
"testsuites",
|
|
401
|
+
name="Warp Tests",
|
|
402
|
+
failures=str(tests_failed),
|
|
403
|
+
errors=str(tests_errored),
|
|
404
|
+
skipped=str(tests_skipped),
|
|
405
|
+
tests=str(tests_run),
|
|
406
|
+
time=f"{test_duration:.3f}",
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
for test_data in test_records:
|
|
410
|
+
test = test_data[0]
|
|
411
|
+
test_duration = test_data[1]
|
|
412
|
+
test_status = test_data[2]
|
|
413
|
+
|
|
414
|
+
test_case = ET.SubElement(
|
|
415
|
+
root,
|
|
416
|
+
"testcase",
|
|
417
|
+
classname=test.__class__.__name__,
|
|
418
|
+
name=test._testMethodName,
|
|
419
|
+
time=f"{test_duration:.3f}",
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
if test_status == "FAIL":
|
|
423
|
+
failure = ET.SubElement(test_case, "failure", message=str(test_data[3]))
|
|
424
|
+
failure.text = str(test_data[4]) # Stacktrace
|
|
425
|
+
elif test_status == "ERROR":
|
|
426
|
+
error = ET.SubElement(test_case, "error")
|
|
427
|
+
error.text = str(test_data[4]) # Stacktrace
|
|
428
|
+
elif test_status == "SKIP":
|
|
429
|
+
skip = ET.SubElement(test_case, "skipped")
|
|
430
|
+
skip.text = str(test_data[3]) # The skip reason
|
|
431
|
+
|
|
432
|
+
tree = ET.ElementTree(root)
|
|
433
|
+
|
|
434
|
+
if hasattr(ET, "indent"):
|
|
435
|
+
ET.indent(root) # Pretty-printed XML output, Python 3.9 required
|
|
436
|
+
|
|
437
|
+
tree.write(outfile, encoding="utf-8", xml_declaration=True)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
class ParallelJunitTestResult(unittest.TextTestResult):
|
|
441
|
+
def __init__(self, stream, descriptions, verbosity):
|
|
442
|
+
stream = type(stream)(sys.stderr)
|
|
443
|
+
self.test_record = []
|
|
444
|
+
super().__init__(stream, descriptions, verbosity)
|
|
445
|
+
|
|
446
|
+
def startTest(self, test):
|
|
447
|
+
if self.showAll:
|
|
448
|
+
self.stream.writeln(f"{self.getDescription(test)} ...")
|
|
449
|
+
self.stream.flush()
|
|
450
|
+
self.start_time = time.perf_counter_ns()
|
|
451
|
+
super(unittest.TextTestResult, self).startTest(test)
|
|
452
|
+
|
|
453
|
+
def _add_helper(self, test, dots_message, show_all_message):
|
|
454
|
+
if self.showAll:
|
|
455
|
+
self.stream.writeln(f"{self.getDescription(test)} ... {show_all_message}")
|
|
456
|
+
elif self.dots:
|
|
457
|
+
self.stream.write(dots_message)
|
|
458
|
+
self.stream.flush()
|
|
459
|
+
|
|
460
|
+
def _record_test(self, test, code, message=None, details=None):
|
|
461
|
+
duration = round((time.perf_counter_ns() - self.start_time) * 1e-9, 3) # [s]
|
|
462
|
+
self.test_record.append((test, duration, code, message, details))
|
|
463
|
+
|
|
464
|
+
def addSuccess(self, test):
|
|
465
|
+
super(unittest.TextTestResult, self).addSuccess(test)
|
|
466
|
+
self._add_helper(test, ".", "ok")
|
|
467
|
+
self._record_test(test, "OK")
|
|
468
|
+
|
|
469
|
+
def addError(self, test, err):
|
|
470
|
+
super(unittest.TextTestResult, self).addError(test, err)
|
|
471
|
+
self._add_helper(test, "E", "ERROR")
|
|
472
|
+
self._record_test(test, "ERROR", str(err[1]), self._exc_info_to_string(err, test))
|
|
473
|
+
|
|
474
|
+
def addFailure(self, test, err):
|
|
475
|
+
super(unittest.TextTestResult, self).addFailure(test, err)
|
|
476
|
+
self._add_helper(test, "F", "FAIL")
|
|
477
|
+
self._record_test(test, "FAIL", str(err[1]), self._exc_info_to_string(err, test))
|
|
478
|
+
|
|
479
|
+
def addSkip(self, test, reason):
|
|
480
|
+
super(unittest.TextTestResult, self).addSkip(test, reason)
|
|
481
|
+
self._add_helper(test, "s", f"skipped {reason!r}")
|
|
482
|
+
self._record_test(test, "SKIP", reason)
|
|
483
|
+
|
|
484
|
+
def addExpectedFailure(self, test, err):
|
|
485
|
+
super(unittest.TextTestResult, self).addExpectedFailure(test, err)
|
|
486
|
+
self._add_helper(test, "x", "expected failure")
|
|
487
|
+
self._record_test(test, "OK", "expected failure")
|
|
488
|
+
|
|
489
|
+
def addUnexpectedSuccess(self, test):
|
|
490
|
+
super(unittest.TextTestResult, self).addUnexpectedSuccess(test)
|
|
491
|
+
self._add_helper(test, "u", "unexpected success")
|
|
492
|
+
self._record_test(test, "FAIL", "unexpected success")
|
|
493
|
+
|
|
494
|
+
def addSubTest(self, test, subtest, err):
|
|
495
|
+
super(unittest.TextTestResult, self).addSubTest(test, subtest, err)
|
|
496
|
+
if err is not None:
|
|
497
|
+
self._add_helper(test, "E", "ERROR")
|
|
498
|
+
# err is (class, error, traceback)
|
|
499
|
+
self._record_test(test, "FAIL", str(err[1]), self._exc_info_to_string(err, test))
|
|
500
|
+
|
|
501
|
+
def printErrors(self):
|
|
502
|
+
pass
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _tc_escape(s):
|
|
506
|
+
"""Modifies strings so they can be used in TeamCity log messages."""
|
|
507
|
+
s = s.replace("|", "||")
|
|
508
|
+
s = s.replace("\n", "|n")
|
|
509
|
+
s = s.replace("\r", "|r")
|
|
510
|
+
s = s.replace("'", "|'")
|
|
511
|
+
s = s.replace("[", "|[")
|
|
512
|
+
s = s.replace("]", "|]")
|
|
513
|
+
return s
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
class ParallelTeamCityTestResult(unittest.TextTestResult):
|
|
517
|
+
def __init__(self, stream, descriptions, verbosity):
|
|
518
|
+
stream = type(stream)(sys.stderr)
|
|
519
|
+
super().__init__(stream, descriptions, verbosity)
|
|
520
|
+
self.test_record = []
|
|
521
|
+
|
|
522
|
+
def startTest(self, test):
|
|
523
|
+
if self.showAll:
|
|
524
|
+
self.stream.writeln(f"{self.getDescription(test)} ...")
|
|
525
|
+
self.stream.flush()
|
|
526
|
+
self.start_time = time.perf_counter_ns()
|
|
527
|
+
super(unittest.TextTestResult, self).startTest(test)
|
|
528
|
+
|
|
529
|
+
def _add_helper(self, test, dots_message, show_all_message):
|
|
530
|
+
if self.showAll:
|
|
531
|
+
self.stream.writeln(f"{self.getDescription(test)} ... {show_all_message}")
|
|
532
|
+
elif self.dots:
|
|
533
|
+
self.stream.write(dots_message)
|
|
534
|
+
self.stream.flush()
|
|
535
|
+
|
|
536
|
+
def addSuccess(self, test):
|
|
537
|
+
super(unittest.TextTestResult, self).addSuccess(test)
|
|
538
|
+
self._add_helper(test, ".", "ok")
|
|
539
|
+
self.reportSuccess(test)
|
|
540
|
+
|
|
541
|
+
def addError(self, test, err):
|
|
542
|
+
super(unittest.TextTestResult, self).addError(test, err)
|
|
543
|
+
self._add_helper(test, "E", "ERROR")
|
|
544
|
+
self.reportFailure(test, err)
|
|
545
|
+
|
|
546
|
+
def addFailure(self, test, err):
|
|
547
|
+
super(unittest.TextTestResult, self).addFailure(test, err)
|
|
548
|
+
self._add_helper(test, "F", "FAIL")
|
|
549
|
+
self.reportFailure(test, err)
|
|
550
|
+
|
|
551
|
+
def addSkip(self, test, reason):
|
|
552
|
+
super(unittest.TextTestResult, self).addSkip(test, reason)
|
|
553
|
+
self._add_helper(test, "s", f"skipped {reason!r}")
|
|
554
|
+
self.reportIgnored(test, reason)
|
|
555
|
+
|
|
556
|
+
def addExpectedFailure(self, test, err):
|
|
557
|
+
super(unittest.TextTestResult, self).addExpectedFailure(test, err)
|
|
558
|
+
self._add_helper(test, "x", "expected failure")
|
|
559
|
+
self.reportSuccess(test)
|
|
560
|
+
|
|
561
|
+
def addUnexpectedSuccess(self, test):
|
|
562
|
+
super(unittest.TextTestResult, self).addUnexpectedSuccess(test)
|
|
563
|
+
self._add_helper(test, "u", "unexpected success")
|
|
564
|
+
self.reportFailure(test, "unexpected success")
|
|
565
|
+
|
|
566
|
+
def addSubTest(self, test, subtest, err):
|
|
567
|
+
super(unittest.TextTestResult, self).addSubTest(test, subtest, err)
|
|
568
|
+
if err is not None:
|
|
569
|
+
self._add_helper(test, "E", "ERROR")
|
|
570
|
+
self.reportSubTestFailure(test, err)
|
|
571
|
+
|
|
572
|
+
def printErrors(self):
|
|
573
|
+
pass
|
|
574
|
+
|
|
575
|
+
def reportIgnored(self, test, reason):
|
|
576
|
+
test_id = test.id()
|
|
577
|
+
self.stream.writeln(f"##teamcity[testIgnored name='{test_id}' message='{_tc_escape(str(reason))}']")
|
|
578
|
+
self.stream.flush()
|
|
579
|
+
|
|
580
|
+
def reportSuccess(self, test):
|
|
581
|
+
duration = round((time.perf_counter_ns() - self.start_time) / 1e6) # [ms]
|
|
582
|
+
test_id = test.id()
|
|
583
|
+
self.stream.writeln(f"##teamcity[testStarted name='{test_id}']")
|
|
584
|
+
self.stream.writeln(f"##teamcity[testFinished name='{test_id}' duration='{duration}']")
|
|
585
|
+
self.stream.flush()
|
|
586
|
+
|
|
587
|
+
def reportFailure(self, test, err):
|
|
588
|
+
test_id = test.id()
|
|
589
|
+
self.stream.writeln(f"##teamcity[testStarted name='{test_id}']")
|
|
590
|
+
self.stream.writeln(
|
|
591
|
+
f"##teamcity[testFailed name='{test_id}' message='{_tc_escape(str(err[1]))}' details='{_tc_escape(self._exc_info_to_string(err, test))}']"
|
|
592
|
+
)
|
|
593
|
+
self.stream.writeln(f"##teamcity[testFinished name='{test_id}']")
|
|
594
|
+
self.stream.flush()
|
|
595
|
+
|
|
596
|
+
def reportSubTestFailure(self, test, err):
|
|
597
|
+
test_id = test.id()
|
|
598
|
+
self.stream.writeln(f"##teamcity[testStarted name='{test_id}']")
|
|
599
|
+
self.stream.writeln(
|
|
600
|
+
f"##teamcity[testFailed name='{test_id}' message='{_tc_escape(str(err[1]))}' details='{_tc_escape(self._exc_info_to_string(err, test))}']"
|
|
601
|
+
)
|
|
602
|
+
self.stream.writeln(f"##teamcity[testFinished name='{test_id}']")
|
|
603
|
+
self.stream.flush()
|