code: def testStructData(self):
      source_data_dir = FLAGS.test_tmpdir
      stats_artifact = standard_artifacts.ExampleStatistics()
      stats_artifact.uri = os.path.join(
          source_data_dir, "statistics_gen"
      )
      stats_artifact.split_names = artifact_utils.encode_split_names(
          ["train", "eval"]
      )
  
      struct_stats_train = text_format.Parse(
          """
        datasets {
          num_examples: 12
          features {
            path {
              step: "parent_feature"
            }
            type: STRUCT
            struct_stats {
              common_stats {
                  num_non_missing: 12
              }
            }
          }
          features {
            path {
              step: "parent_feature"
              step: "value_feature"
            }
            type: INT
            num_stats {
              common_stats {
                  num_non_missing: 12
              }
              histograms {
                buckets {
                  low_value: 0.0
                  high_value: 9.0
                  sample_count: 12.0
                }
                type: STANDARD
              }
            }
          }
        }
        """,
          statistics_pb2.DatasetFeatureStatisticsList(),
      )
  
      struct_stats_eval = text_format.Parse(
          """
          datasets {
            num_examples: 12
            features {
              path {
                step: "parent_feature"
              }
              type: STRUCT
              struct_stats {
                common_stats {
                    num_non_missing: 12
                }
              }
            }
            features {
              path {
                step: "parent_feature"
                step: "value_feature"
              }
              type: INT
              num_stats {
                common_stats {
                    num_non_missing: 12
                }
                histograms {
                  buckets {
                    low_value: 10.0
                    high_value: 19.0
                    sample_count: 12.0
                  }
                  type: STANDARD
                }
              }
            }
          }
          """,
          statistics_pb2.DatasetFeatureStatisticsList(),
      )
  
      validation_config = text_format.Parse(
          """
        default_slice_config: {
          feature: {
              path: {
                  step: 'parent_feature'
                  step: 'value_feature'
              }
              distribution_comparator: {
                jensen_shannon_divergence: {
                    threshold: 0.0
                }
              }
          }
        }""",
          distribution_validator_pb2.DistributionValidatorConfig(),
      )
  
      expected_anomalies = text_format.Parse(
          """
        anomaly_info {
          key: "parent_feature.value_feature"
          value {
            severity: ERROR
            reason {
              type: COMPARATOR_JENSEN_SHANNON_DIVERGENCE_HIGH
              short_description: "High approximate Jensen-Shannon divergence between current and previous"
              description: "The approximate Jensen-Shannon divergence between current and previous is 1 (up to six significant digits), above the threshold 0."
            }
            path {
              step: "parent_feature"
              step: "value_feature"
            }
          }
        }
        anomaly_name_format: SERIALIZED_PATH
        drift_skew_info {
          path {
            step: "parent_feature"
            step: "value_feature"
          }
          drift_measurements {
            type: JENSEN_SHANNON_DIVERGENCE
            value: 1.0
            threshold: 0.0
          }
        }""",
          anomalies_pb2.Anomalies(),
      )
  
      expected_alerts = component_generated_alert_pb2.ComponentGeneratedAlertList(
          component_generated_alert_list=[
              component_generated_alert_pb2.ComponentGeneratedAlertInfo(
                  alert_name=(
                      "[train_eval] High approximate Jensen-Shannon divergence "
                      "between current and previous"
                  ),
                  alert_body=(
                      "[train_eval] The approximate Jensen-Shannon divergence "
                      "between current and previous is 1 (up to six significant "
                      "digits), above the threshold 0."
                  ),
              )
          ],
      )
  
      # Create stats artifacts with a struct feature.
      for split_dir in ["Split-eval", "Split-train"]:
          full_split_dir = os.path.join(stats_artifact.uri, split_dir)
          fileio.makedirs(full_split_dir)
          stats_path = os.path.join(full_split_dir, "FeatureStats.pb")
          if split_dir == "Split-eval":
              io_utils.write_bytes_file(
                  stats_path, struct_stats_eval.SerializeToString()
              )
          else:
              io_utils.write_bytes_file(
                  stats_path, struct_stats_train.SerializeToString()
              )
  
      output_data_dir = os.path.join(
          os.environ.get(
              "TEST_UNDECLARED_OUTPUTS_DIR", self.get_temp_dir()
          ),
          self._testMethodName,
      )
  
      validation_output = standard_artifacts.ExampleAnomalies()
      validation_output.uri = os.path.join(output_data_dir, "output")
  
      input_dict = {
          standard_component_specs.STATISTICS_KEY: [stats_artifact],
          standard_component_specs.BASELINE_STATISTICS_KEY: [
              stats_artifact
          ],
      }
  
      # The analyzed splits are set for this test to get a single result proto.
      exec_properties = {
          # List needs to be serialized before being passed into Do function.
          standard_component_specs.INCLUDE_SPLIT_PAIRS_KEY: json_utils.dumps(
              [("train", "eval")]
          ),
          standard_component_specs.DISTRIBUTION_VALIDATOR_CONFIG_KEY: validation_config,
      }
  
      output_dict = {
          standard_component_specs.ANOMALIES_KEY: [validation_output],
      }
  
      distribution_validator_executor = executor.Executor()
      executor_output = distribution_validator_executor.Do(
          input_dict, output_dict, exec_properties
      )
  
      self.assertEqual(
          artifact_utils.encode_split_names(["train_eval"]),
          validation_output.split_names,
      )
  
      distribution_anomalies_path = os.path.join(
          validation_output.uri, "SplitPair-train_eval", "SchemaDiff.pb"
      )
      self.assertTrue(fileio.exists(distribution_anomalies_path))
      distribution_anomalies_bytes = io_utils.read_bytes_file(
          distribution_anomalies_path
      )
      distribution_anomalies = anomalies_pb2.Anomalies()
      distribution_anomalies.ParseFromString(
          distribution_anomalies_bytes
      )
      self.assertEqualExceptBaseline(
          expected_anomalies, distribution_anomalies
      )
  
      actual_alerts = (
          component_generated_alert_pb2.ComponentGeneratedAlertList()
      )
      executor_output.execution_properties[
          constants.COMPONENT_GENERATED_ALERTS_KEY
      ].proto_value.Unpack(actual_alerts)
      self.assertEqual(actual_alerts, expected_alerts)
  
imports: import os
  import tempfile
  from absl import flags
  from absl.testing import absltest
  from absl.testing import parameterized
  from tensorflow_data_validation.anomalies.proto import custom_validation_config_pb2
  from tfx.components.distribution_validator import executor
  from tfx.dsl.io import fileio
  from tfx.orchestration.experimental.core import component_generated_alert_pb2
  from tfx.orchestration.experimental.core import constants
  from tfx.proto import distribution_validator_pb2
  from tfx.types import artifact_utils
  from tfx.types import standard_artifacts
  from tfx.types import standard_component_specs
  from tfx.utils import io_utils
  from tfx.utils import json_utils
  from tfx.utils import test_case_utils
  from google.protobuf import text_format
  from tensorflow_metadata.proto.v0 import anomalies_pb2
  from tensorflow_metadata.proto.v0 import statistics_pb2
label_desc: Use image node_id 6 for calling the ExecutorTest obj's underlying member method code with example usage: obj.testStructData() without return types