code: def test_conv2d_layer(
      img_height: int,
      img_width: int,
      in_channels: int,
      out_channels: int,
      kernel_size: int,
      stride: int,
      padding: Union[int, Tuple[int], str],
      dilation: Union[int, Tuple[int]],
      norm: str,
      pool_kernel_size: Union[int, Tuple[int]],
      pool_stride: int,
      pool_padding: Union[int, Tuple[int], str],
      pool_dilation: Union[int, Tuple[int]],
  ) -> None:
      conv2d_layer = Conv2DLayer(
          img_height=img_height,
          img_width=img_width,
          in_channels=in_channels,
          out_channels=out_channels,
          kernel_size=kernel_size,
          stride=stride,
          padding=padding,
          dilation=dilation,
          norm=norm,
          pool_kernel_size=pool_kernel_size,
          pool_stride=pool_stride,
          pool_padding=pool_padding,
          pool_dilation=pool_dilation,
      )
      input_tensor = torch.rand(2, in_channels, img_height, img_width)
      output_tensor = conv2d_layer(input_tensor)
      assert output_tensor.shape[1:] == conv2d_layer.output_shape
  
file_path: ludwig/tests/ludwig/modules/test_convolutional_modules.py
imports: from typing import Callable, Dict, List, Optional, Tuple, Union
  import pytest
  import torch
  from ludwig.modules.convolutional_modules import Conv1DLayer, Conv1DStack, Conv2DLayer, Conv2DLayerFixedPadding, Conv2DStack, ParallelConv1D, ParallelConv1DStack, ResNet, ResNetBlock, ResNetBlockLayer, ResNetBottleneckBlock
  from ludwig.utils.image_utils import get_img_output_shape
  from tests.integration_tests.parameter_update_utils import check_module_parameters_updated
label_desc: Use image node_id 6 for calling a global function with example usage: test_conv2d_layer(img_height, img_width, in_channels, out_channels, kernel_size, stride, padding, dilation, norm, pool_kernel_size, pool_stride, pool_padding, pool_dilation) without return types