Add run_identifier property to BetoCQ v2 tests

Also fix failure message formatting in FixedWifiMediumFunctionTestActor

Bug: 323080113
Test: local
Change-Id: I77d3cc24c9b1dace323b12621fe6e2f5f9a30f40
diff --git a/tests/bettertogether/betocq/function_tests/fixed_wifi_medium_function_test_actor.py b/tests/bettertogether/betocq/function_tests/fixed_wifi_medium_function_test_actor.py
index 2f03539..d44e04e 100644
--- a/tests/bettertogether/betocq/function_tests/fixed_wifi_medium_function_test_actor.py
+++ b/tests/bettertogether/betocq/function_tests/fixed_wifi_medium_function_test_actor.py
@@ -125,16 +125,18 @@
         self._test_failure_reason
         == nc_constants.SingleTestFailureReason.WIFI_MEDIUM_UPGRADE
     ):
-      return f'{self._test_failure_reason.name} - '.join(
+      return ''.join([
+          f'{self._test_failure_reason.name} - ',
           self._get_medium_upgrade_failure_tip()
-      )
+      ])
     if (
         self._test_failure_reason
         == nc_constants.SingleTestFailureReason.FILE_TRANSFER_FAIL
     ):
-      return f'{self._test_failure_reason.name} - '.join(
+      return ''.join([
+          f'{self._test_failure_reason.name} - ',
           self._get_file_transfer_failure_tip()
-      )
+      ])
     return ''.join([
         f'{self._test_failure_reason.name} - ',
         nc_constants.COMMON_TRIAGE_TIP.get(self._test_failure_reason),
diff --git a/tests/bettertogether/betocq/nc_base_test.py b/tests/bettertogether/betocq/nc_base_test.py
index b8452d8..2c862e7 100644
--- a/tests/bettertogether/betocq/nc_base_test.py
+++ b/tests/bettertogether/betocq/nc_base_test.py
@@ -33,6 +33,7 @@
 from betocq import android_wifi_utils
 from betocq import nc_constants
 from betocq import setup_utils
+from betocq import version
 
 NEARBY_SNIPPET_PACKAGE_NAME = 'com.google.android.nearby.mobly.snippet'
 NEARBY_SNIPPET_2_PACKAGE_NAME = 'com.google.android.nearby.mobly.snippet.second'
@@ -44,6 +45,8 @@
 class NCBaseTestClass(base_test.BaseTestClass):
   """The Base of Nearby Connection E2E tests."""
 
+  _run_identifier_is_set = False
+
   def __init__(self, configs):
     super().__init__(configs)
     self.ads: list[android_device.AndroidDevice] = []
@@ -64,6 +67,7 @@
     return None
 
   def setup_class(self) -> None:
+    self._set_run_identifier()
     self._setup_openwrt_wifi()
     self.ads = self.register_controller(android_device, min_number=2)
     try:
@@ -114,6 +118,24 @@
         raise_on_exception=True,
     )
 
+  def _set_run_identifier(self) -> None:
+    """Set a run_identifier property describing the test run context.
+
+    This property is only set once, even if multiple test classes are run as
+    part of a test suite.
+    """
+    if NCBaseTestClass._run_identifier_is_set:
+      return
+    run_identifier = {}
+    run_identifier['test_version'] = version.TEST_SCRIPT_VERSION
+    run_identifier['target_cuj'] = self.test_parameters.target_cuj_name
+    run_identifier_str = ', '.join(
+        [f'{key}:{value}' for key, value in run_identifier.items()]
+    )
+    run_identifier_str = f'{{{run_identifier_str}}}'
+    self.record_data({'properties': {'run_identifier': run_identifier_str}})
+    NCBaseTestClass._run_identifier_is_set = True
+
   def _setup_openwrt_wifi(self):
     """Sets up the wifi connection with OpenWRT."""
     if not self.user_params.get('use_auto_controlled_wifi_ap', False):