github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/kubernetes/debugging/transform_python_test.go (about) 1 /* 2 Copyright 2021 The Skaffold Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package debugging 18 19 import ( 20 "testing" 21 22 appsv1 "k8s.io/api/apps/v1" 23 batchv1 "k8s.io/api/batch/v1" 24 v1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 28 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug" 29 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/debug/types" 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/debugging/adapter" 31 "github.com/GoogleContainerTools/skaffold/testutil" 32 ) 33 34 func TestPythonTransformer_Apply(t *testing.T) { 35 tests := []struct { 36 description string 37 containerSpec v1.Container 38 configuration debug.ImageConfiguration 39 overrideProtocols []string 40 shouldErr bool 41 result v1.Container 42 debugConfig types.ContainerDebugConfiguration 43 image string 44 }{ 45 { 46 description: "empty", 47 containerSpec: v1.Container{}, 48 configuration: debug.ImageConfiguration{}, 49 result: v1.Container{}, 50 shouldErr: true, 51 }, 52 { 53 description: "basic", 54 containerSpec: v1.Container{}, 55 configuration: debug.ImageConfiguration{Entrypoint: []string{"python"}}, 56 result: v1.Container{ 57 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python"}, 58 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 59 }, 60 debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}}, 61 image: "python", 62 }, 63 { 64 description: "override protocol - pydevd, dap", 65 containerSpec: v1.Container{}, 66 configuration: debug.ImageConfiguration{Entrypoint: []string{"python"}}, 67 overrideProtocols: []string{"pydevd", "dap"}, 68 result: v1.Container{ 69 Command: []string{"/dbg/python/launcher", "--mode", "pydevd", "--port", "5678", "--", "python"}, 70 Ports: []v1.ContainerPort{{Name: "pydevd", ContainerPort: 5678}}, 71 }, 72 debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"pydevd": 5678}}, 73 image: "python", 74 }, 75 { 76 description: "override protocol - dap, pydevd", 77 containerSpec: v1.Container{}, 78 configuration: debug.ImageConfiguration{Entrypoint: []string{"python"}}, 79 overrideProtocols: []string{"dap", "pydevd"}, 80 result: v1.Container{ 81 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python"}, 82 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 83 }, 84 debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}}, 85 image: "python", 86 }, 87 { 88 description: "existing port", 89 containerSpec: v1.Container{ 90 Ports: []v1.ContainerPort{{Name: "http-server", ContainerPort: 8080}}, 91 }, 92 configuration: debug.ImageConfiguration{Entrypoint: []string{"python"}}, 93 result: v1.Container{ 94 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python"}, 95 Ports: []v1.ContainerPort{{Name: "http-server", ContainerPort: 8080}, {Name: "dap", ContainerPort: 5678}}, 96 }, 97 debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}}, 98 image: "python", 99 }, 100 { 101 description: "command not entrypoint", 102 containerSpec: v1.Container{}, 103 configuration: debug.ImageConfiguration{Arguments: []string{"python"}}, 104 result: v1.Container{ 105 Args: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python"}, 106 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 107 }, 108 debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}}, 109 image: "python", 110 }, 111 { 112 description: "entrypoint with python env vars", 113 containerSpec: v1.Container{}, 114 configuration: debug.ImageConfiguration{Entrypoint: []string{"foo"}, Env: map[string]string{"PYTHON_VERSION": "3"}}, 115 result: v1.Container{ 116 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "foo"}, 117 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 118 }, 119 debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}}, 120 image: "python", 121 }, 122 { 123 description: "command with python env vars", 124 containerSpec: v1.Container{}, 125 configuration: debug.ImageConfiguration{Arguments: []string{"foo"}, Env: map[string]string{"PYTHON_VERSION": "3"}}, 126 result: v1.Container{ 127 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--"}, 128 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 129 }, 130 debugConfig: types.ContainerDebugConfiguration{Runtime: "python", Ports: map[string]uint32{"dap": 5678}}, 131 image: "python", 132 }, 133 } 134 var identity debug.PortAllocator = func(port int32) int32 { 135 return port 136 } 137 for _, test := range tests { 138 testutil.Run(t, test.description, func(t *testutil.T) { 139 adapter := adapter.NewAdapter(&test.containerSpec) 140 config, image, err := debug.NewPythonTransformer().Apply(adapter, test.configuration, identity, test.overrideProtocols) 141 adapter.Apply() 142 143 t.CheckError(test.shouldErr, err) 144 t.CheckDeepEqual(test.result, test.containerSpec) 145 t.CheckDeepEqual(test.debugConfig, config) 146 t.CheckDeepEqual(test.image, image) 147 }) 148 } 149 } 150 151 func TestTransformManifestPython(t *testing.T) { 152 int32p := func(x int32) *int32 { return &x } 153 tests := []struct { 154 description string 155 in runtime.Object 156 transformed bool 157 out runtime.Object 158 }{ 159 { 160 "Pod with no transformable container", 161 &v1.Pod{ 162 Spec: v1.PodSpec{Containers: []v1.Container{ 163 { 164 Name: "test", 165 Command: []string{"echo", "Hello World"}, 166 }, 167 }}}, 168 false, 169 &v1.Pod{ 170 Spec: v1.PodSpec{Containers: []v1.Container{ 171 { 172 Name: "test", 173 Command: []string{"echo", "Hello World"}, 174 }, 175 }}}, 176 }, 177 { 178 "Pod with ptvsd", 179 &v1.Pod{ 180 Spec: v1.PodSpec{ 181 Containers: []v1.Container{{ 182 Name: "test", 183 Command: []string{"python", "-mptvsd", "--host", "0.0.0.0", "--port", "5678", "foo.py"}, 184 }}, 185 }}, 186 true, 187 &v1.Pod{ 188 ObjectMeta: metav1.ObjectMeta{ 189 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`}, 190 }, 191 Spec: v1.PodSpec{ 192 Containers: []v1.Container{{ 193 Name: "test", 194 Command: []string{"python", "-mptvsd", "--host", "0.0.0.0", "--port", "5678", "foo.py"}, 195 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 196 }}, 197 }}, 198 }, 199 { 200 "Pod with debugpy", 201 &v1.Pod{ 202 Spec: v1.PodSpec{ 203 Containers: []v1.Container{{ 204 Name: "test", 205 Command: []string{"python", "-mdebugpy", "--listen", "5678", "foo.py"}, 206 }}, 207 }}, 208 true, 209 &v1.Pod{ 210 ObjectMeta: metav1.ObjectMeta{ 211 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`}, 212 }, 213 Spec: v1.PodSpec{ 214 Containers: []v1.Container{{ 215 Name: "test", 216 Command: []string{"python", "-mdebugpy", "--listen", "5678", "foo.py"}, 217 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 218 }}, 219 }}, 220 }, 221 { 222 "Pod with Python container", 223 &v1.Pod{ 224 Spec: v1.PodSpec{ 225 Containers: []v1.Container{{ 226 Name: "test", 227 Command: []string{"python", "foo.py"}, 228 }}, 229 }}, 230 true, 231 &v1.Pod{ 232 ObjectMeta: metav1.ObjectMeta{ 233 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`}, 234 }, 235 Spec: v1.PodSpec{ 236 Containers: []v1.Container{{ 237 Name: "test", 238 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"}, 239 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 240 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 241 }}, 242 InitContainers: []v1.Container{{ 243 Name: "install-python-debug-support", 244 Image: "HELPERS/python", 245 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 246 }}, 247 Volumes: []v1.Volume{{ 248 Name: "debugging-support-files", 249 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 250 }}, 251 }}, 252 }, 253 { 254 "Deployment with Python container", 255 &appsv1.Deployment{ 256 Spec: appsv1.DeploymentSpec{ 257 Replicas: int32p(2), 258 Template: v1.PodTemplateSpec{ 259 Spec: v1.PodSpec{ 260 Containers: []v1.Container{{ 261 Name: "test", 262 Command: []string{"python", "foo.py"}, 263 }}, 264 }}}}, 265 true, 266 &appsv1.Deployment{ 267 // ObjectMeta: metav1.ObjectMeta{ 268 // Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`}, 269 // }, 270 Spec: appsv1.DeploymentSpec{ 271 Replicas: int32p(1), 272 Template: v1.PodTemplateSpec{ 273 ObjectMeta: metav1.ObjectMeta{ 274 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`}, 275 }, 276 Spec: v1.PodSpec{ 277 Containers: []v1.Container{{ 278 Name: "test", 279 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"}, 280 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 281 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 282 }}, 283 InitContainers: []v1.Container{{ 284 Name: "install-python-debug-support", 285 Image: "HELPERS/python", 286 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 287 }}, 288 Volumes: []v1.Volume{{ 289 Name: "debugging-support-files", 290 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 291 }}, 292 }}}}, 293 }, 294 { 295 "ReplicaSet with Python container", 296 &appsv1.ReplicaSet{ 297 Spec: appsv1.ReplicaSetSpec{ 298 Replicas: int32p(2), 299 Template: v1.PodTemplateSpec{ 300 Spec: v1.PodSpec{ 301 Containers: []v1.Container{{ 302 Name: "test", 303 Command: []string{"python", "foo.py"}, 304 }}, 305 }}}}, 306 true, 307 &appsv1.ReplicaSet{ 308 // ObjectMeta: metav1.ObjectMeta{ 309 // Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`}, 310 // }, 311 Spec: appsv1.ReplicaSetSpec{ 312 Replicas: int32p(1), 313 Template: v1.PodTemplateSpec{ 314 ObjectMeta: metav1.ObjectMeta{ 315 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`}, 316 }, 317 Spec: v1.PodSpec{ 318 Containers: []v1.Container{{ 319 Name: "test", 320 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"}, 321 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 322 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 323 }}, 324 InitContainers: []v1.Container{{ 325 Name: "install-python-debug-support", 326 Image: "HELPERS/python", 327 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 328 }}, 329 Volumes: []v1.Volume{{ 330 Name: "debugging-support-files", 331 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 332 }}, 333 }}}}, 334 }, 335 { 336 "StatefulSet with Python container", 337 &appsv1.StatefulSet{ 338 Spec: appsv1.StatefulSetSpec{ 339 Replicas: int32p(2), 340 Template: v1.PodTemplateSpec{ 341 Spec: v1.PodSpec{ 342 Containers: []v1.Container{{ 343 Name: "test", 344 Command: []string{"python", "foo.py"}, 345 }}, 346 }}}}, 347 true, 348 &appsv1.StatefulSet{ 349 // ObjectMeta: metav1.ObjectMeta{ 350 // Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`}, 351 // }, 352 Spec: appsv1.StatefulSetSpec{ 353 Replicas: int32p(1), 354 Template: v1.PodTemplateSpec{ 355 ObjectMeta: metav1.ObjectMeta{ 356 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`}, 357 }, 358 Spec: v1.PodSpec{ 359 Containers: []v1.Container{{ 360 Name: "test", 361 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"}, 362 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 363 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 364 }}, 365 InitContainers: []v1.Container{{ 366 Name: "install-python-debug-support", 367 Image: "HELPERS/python", 368 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 369 }}, 370 Volumes: []v1.Volume{{ 371 Name: "debugging-support-files", 372 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 373 }}, 374 }}}}, 375 }, 376 { 377 "DaemonSet with Python container", 378 &appsv1.DaemonSet{ 379 Spec: appsv1.DaemonSetSpec{ 380 Template: v1.PodTemplateSpec{ 381 Spec: v1.PodSpec{ 382 Containers: []v1.Container{{ 383 Name: "test", 384 Command: []string{"python", "foo.py"}, 385 }}, 386 }}}}, 387 true, 388 &appsv1.DaemonSet{ 389 // ObjectMeta: metav1.ObjectMeta{ 390 // Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`}, 391 // }, 392 Spec: appsv1.DaemonSetSpec{ 393 Template: v1.PodTemplateSpec{ 394 ObjectMeta: metav1.ObjectMeta{ 395 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`}, 396 }, 397 Spec: v1.PodSpec{ 398 Containers: []v1.Container{{ 399 Name: "test", 400 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"}, 401 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 402 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 403 }}, 404 InitContainers: []v1.Container{{ 405 Name: "install-python-debug-support", 406 Image: "HELPERS/python", 407 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 408 }}, 409 Volumes: []v1.Volume{{ 410 Name: "debugging-support-files", 411 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 412 }}, 413 }}}}, 414 }, 415 { 416 "Job with Python container", 417 &batchv1.Job{ 418 Spec: batchv1.JobSpec{ 419 Template: v1.PodTemplateSpec{ 420 Spec: v1.PodSpec{ 421 Containers: []v1.Container{{ 422 Name: "test", 423 Command: []string{"python", "foo.py"}, 424 }}, 425 }}}}, 426 true, 427 &batchv1.Job{ 428 // ObjectMeta: metav1.ObjectMeta{ 429 // Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`}, 430 // }, 431 Spec: batchv1.JobSpec{ 432 Template: v1.PodTemplateSpec{ 433 ObjectMeta: metav1.ObjectMeta{ 434 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`}, 435 }, 436 Spec: v1.PodSpec{ 437 Containers: []v1.Container{{ 438 Name: "test", 439 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"}, 440 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 441 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 442 }}, 443 InitContainers: []v1.Container{{ 444 Name: "install-python-debug-support", 445 Image: "HELPERS/python", 446 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 447 }}, 448 Volumes: []v1.Volume{{ 449 Name: "debugging-support-files", 450 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 451 }}, 452 }}}}, 453 }, 454 { 455 "ReplicationController with Python container", 456 &v1.ReplicationController{ 457 Spec: v1.ReplicationControllerSpec{ 458 Replicas: int32p(2), 459 Template: &v1.PodTemplateSpec{ 460 Spec: v1.PodSpec{ 461 Containers: []v1.Container{{ 462 Name: "test", 463 Command: []string{"python", "foo.py"}, 464 }}, 465 }}}}, 466 true, 467 &v1.ReplicationController{ 468 // ObjectMeta: metav1.ObjectMeta{ 469 // Labels: map[string]string{"debug.cloud.google.com/enabled": `yes`}, 470 // }, 471 Spec: v1.ReplicationControllerSpec{ 472 Replicas: int32p(1), 473 Template: &v1.PodTemplateSpec{ 474 ObjectMeta: metav1.ObjectMeta{ 475 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`}, 476 }, 477 Spec: v1.PodSpec{ 478 Containers: []v1.Container{{ 479 Name: "test", 480 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"}, 481 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 482 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 483 }}, 484 InitContainers: []v1.Container{{ 485 Name: "install-python-debug-support", 486 Image: "HELPERS/python", 487 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 488 }}, 489 Volumes: []v1.Volume{{ 490 Name: "debugging-support-files", 491 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 492 }}, 493 }}}}, 494 }, 495 { 496 "PodList with Python and non-Python container", 497 &v1.PodList{ 498 Items: []v1.Pod{ 499 { 500 Spec: v1.PodSpec{ 501 Containers: []v1.Container{{ 502 Name: "echo", 503 Command: []string{"echo", "Hello World"}, 504 }}, 505 }}, 506 { 507 Spec: v1.PodSpec{ 508 Containers: []v1.Container{{ 509 Name: "test", 510 Command: []string{"python", "foo.py"}, 511 }}, 512 }}, 513 }}, 514 true, 515 &v1.PodList{ 516 Items: []v1.Pod{ 517 { 518 Spec: v1.PodSpec{ 519 Containers: []v1.Container{{ 520 Name: "echo", 521 Command: []string{"echo", "Hello World"}, 522 }}, 523 }}, 524 { 525 ObjectMeta: metav1.ObjectMeta{ 526 Annotations: map[string]string{"debug.cloud.google.com/config": `{"test":{"runtime":"python","ports":{"dap":5678}}}`}, 527 }, 528 Spec: v1.PodSpec{ 529 Containers: []v1.Container{{ 530 Name: "test", 531 Command: []string{"/dbg/python/launcher", "--mode", "debugpy", "--port", "5678", "--", "python", "foo.py"}, 532 Ports: []v1.ContainerPort{{Name: "dap", ContainerPort: 5678}}, 533 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 534 }}, 535 InitContainers: []v1.Container{{ 536 Name: "install-python-debug-support", 537 Image: "HELPERS/python", 538 VolumeMounts: []v1.VolumeMount{{Name: "debugging-support-files", MountPath: "/dbg"}}, 539 }}, 540 Volumes: []v1.Volume{{ 541 Name: "debugging-support-files", 542 VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, 543 }}, 544 }}, 545 }}, 546 }, 547 } 548 for _, test := range tests { 549 testutil.Run(t, test.description, func(t *testutil.T) { 550 value := test.in.DeepCopyObject() 551 552 retriever := func(image string) (debug.ImageConfiguration, error) { 553 return debug.ImageConfiguration{}, nil 554 } 555 result := transformManifest(value, retriever, "HELPERS") 556 557 t.CheckDeepEqual(test.transformed, result) 558 t.CheckDeepEqual(test.out, value) 559 }) 560 } 561 }