[fuchsia] Refactor the femu recipe

This reduces the nesting level of each step and makes it simpler to edit the recipe.

Change-Id: I04c672b5a88447e4ef44247ec26f35f3ed6e12a2
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/40480
Reviewed-by: Zach Anderson <zra@google.com>
Reviewed-by: Anthony Fandrianto <atyfto@google.com>
Commit-Queue: Sébastien Marchand <sebmarchand@google.com>
diff --git a/recipes/engine/femu_test.py b/recipes/engine/femu_test.py
index 0bef8e6..1963256 100644
--- a/recipes/engine/femu_test.py
+++ b/recipes/engine/femu_test.py
@@ -116,7 +116,6 @@
   return manifest_data['id']
 
 
-# TODO(yuanzhi) Move this logic to vdl recipe_module
 def CasRoot(api):
   """Create a CAS containing flutter test components and fuchsia runfiles for FEMU."""
   sdk_version = GetFuchsiaBuildId(api)
@@ -179,6 +178,108 @@
   return test_suites, root_dir, cas_hash
 
 
+def RunTestSuiteOnFfxEmuImpl(api, suite, ffx, arch):
+  # Launch the emulator
+  if arch == 'arm64':
+    launch_step = api.step(
+        'launch arm64 emulator with QEMU engine', [
+            ffx, '-v', 'emu', 'start', 'terminal.qemu-arm64', '--engine',
+            'qemu', '--headless', '--startup-timeout', '360'
+        ]
+    )
+  else:
+    launch_step = api.step(
+        'launch x64 emulator',
+        [ffx, '-v', 'emu', 'start', 'terminal.qemu-x64', '--headless']
+    )
+
+  # Output information for current emulator
+  # Contains version, product information, etc.
+  api.step('list all targets in the collection', [ffx, 'target', 'list'])
+  api.step('retrieve femu information', [ffx, 'target', 'show'])
+  ffx_repo_list_json = api.step(
+      'get repository information',
+      [ffx, '--machine', 'json', 'repository', 'list'],
+      stdout=api.json.output()
+  ).stdout
+  ffx_repo_path = ffx_repo_list_json[0]['spec']['path']
+
+  for package in suite['package_basenames']:
+    api.step(
+        'ffx repository publish {}'.format(package), [
+            ffx, 'repository', 'publish', ffx_repo_path, '--package-archive',
+            package
+        ]
+    )
+
+  api.step('start FFX repository', [ffx, 'repository', 'server', 'start'])
+  api.step(
+      'Register repository',
+      [ffx, 'target', 'repository', 'register', '--alias', 'fuchsia.com']
+  )
+
+  # Run the actual test
+  # Test command is guaranteed to be well-formed
+  # TODO(http://fxb/121613): Emulator instances are not cleaned up
+  # when tests fail. Added a clean up step before tests start to
+  # stop all running emulators.
+  with api.step.defer_results():
+    api.retry.step('run ffx test', [ffx] + suite['test_command'].split(' '))
+
+
+def ReadLogFiles(api):
+  """
+  Read the log files generated by ffx and by the Fuchsia emulator and present
+  them in the test result page.
+  """
+  with api.step.nest('logs') as dump_step:
+    dump_step.presentation.logs['ffx_daemon_log'] = api.file.read_text(
+        'read ffx daemon log',
+        api.path.join(
+            api.context.env.get('FFX_ISOLATE_DIR'), 'cache', 'logs',
+            'ffx.daemon.log'
+        )
+    )
+    dump_step.presentation.logs['ffx_log'] = api.file.read_text(
+        'read ffx log',
+        api.path.join(
+            api.context.env.get('FFX_ISOLATE_DIR'), 'cache', 'logs', 'ffx.log'
+        )
+    )
+    dump_step.presentation.logs['emulator_log'] = api.file.read_text(
+        'read ffx daemon log',
+        api.path.join(
+            api.context.env.get('FFX_ISOLATE_DIR'), 'data', 'emu', 'instances',
+            'fuchsia-emulator', 'emulator.log'
+        )
+    )
+    dump_step.presentation.logs['emulator_serial_log'] = api.file.read_text(
+        'read ffx daemon log',
+        api.path.join(
+            api.context.env.get('FFX_ISOLATE_DIR'), 'data', 'emu', 'instances',
+            'fuchsia-emulator', 'emulator.log.serial'
+        )
+    )
+
+
+def CleanupAfterTestRun(api, ffx, arch):
+  ReadLogFiles(api)
+  # Cleans up running processes to prevent clashing with future test runs
+  api.step('stop FFX repository', [ffx, 'repository', 'server', 'stop'])
+  api.step('stop {} emulator'.format(arch), [ffx, '-v', 'emu', 'stop', '--all'])
+
+
+def RunTestSuiteOnFfxEmu(api, suite, ffx, arch):
+  """
+  Run the Fuchsia test suite |suite| on the Fuchsia emulator, using ffx.
+  """
+  with api.step.nest('run %s' % suite['name']):
+    try:
+      RunTestSuiteOnFfxEmuImpl(api, suite, ffx, arch)
+    finally:
+      CleanupAfterTestRun(api, ffx, arch)
+
+
 def TestFuchsiaFEMU(api):
   """Run flutter tests on FEMU."""
   test_suites, root_dir, cas_hash = CasRoot(api)
@@ -243,101 +344,7 @@
 
   with api.context(cwd=root_dir), api.step.nest('run FEMU test on %s' % arch):
     for suite in test_suites:
-      with api.step.nest('run %s' % suite['name']):
-        try:
-          # Launch the emulator
-          if arch == 'arm64':
-            launch_step = api.step(
-                'launch arm64 emulator with QEMU engine', [
-                    ffx, '-v', 'emu', 'start', 'terminal.qemu-arm64',
-                    '--engine', 'qemu', '--headless', '--startup-timeout', '360'
-                ]
-            )
-          else:
-            launch_step = api.step(
-                'launch x64 emulator',
-                [ffx, '-v', 'emu', 'start', 'terminal.qemu-x64', '--headless']
-            )
-
-          # Output information for current emulator
-          # Contains version, product information, etc.
-          api.step(
-              'list all targets in the collection', [ffx, 'target', 'list']
-          )
-          api.step('retrieve femu information', [ffx, 'target', 'show'])
-          ffx_repo_list_json = api.step(
-              'get repository information',
-              [ffx, '--machine', 'json', 'repository', 'list'],
-              stdout=api.json.output()
-          ).stdout
-          ffx_repo_path = ffx_repo_list_json[0]['spec']['path']
-
-          for package in suite['package_basenames']:
-            api.step(
-                'ffx repository publish {}'.format(package), [
-                    ffx, 'repository', 'publish', ffx_repo_path,
-                    '--package-archive', package
-                ]
-            )
-
-          api.step(
-              'start FFX repository', [ffx, 'repository', 'server', 'start']
-          )
-          api.step(
-              'Register repository', [
-                  ffx, 'target', 'repository', 'register', '--alias',
-                  'fuchsia.com'
-              ]
-          )
-
-          # Run the actual test
-          # Test command is guaranteed to be well-formed
-          # TODO(http://fxb/121613): Emulator instances are not cleaned up
-          # when tests fail. Added a clean up step before tests start to
-          # stop all running emulators.
-          with api.step.defer_results():
-            api.retry.step(
-                'run ffx test', [ffx] + suite['test_command'].split(' ')
-            )
-
-        finally:
-          with api.step.nest('logs') as dump_step:
-            dump_step.presentation.logs['ffx_daemon_log'] = api.file.read_text(
-                'read ffx daemon log',
-                api.path.join(
-                    api.context.env.get('FFX_ISOLATE_DIR'), 'cache', 'logs',
-                    'ffx.daemon.log'
-                )
-            )
-            dump_step.presentation.logs['ffx_log'] = api.file.read_text(
-                'read ffx log',
-                api.path.join(
-                    api.context.env.get('FFX_ISOLATE_DIR'), 'cache', 'logs',
-                    'ffx.log'
-                )
-            )
-            dump_step.presentation.logs['emulator_log'] = api.file.read_text(
-                'read ffx daemon log',
-                api.path.join(
-                    api.context.env.get('FFX_ISOLATE_DIR'), 'data', 'emu',
-                    'instances', 'fuchsia-emulator', 'emulator.log'
-                )
-            )
-            dump_step.presentation.logs['emulator_serial_log'] = api.file.read_text(
-                'read ffx daemon log',
-                api.path.join(
-                    api.context.env.get('FFX_ISOLATE_DIR'), 'data', 'emu',
-                    'instances', 'fuchsia-emulator', 'emulator.log.serial'
-                )
-            )
-
-          # Cleans up running processes to prevent clashing with future test runs
-          api.step(
-              'stop FFX repository', [ffx, 'repository', 'server', 'stop']
-          )
-          api.step(
-              'stop %s emulator' % arch, [ffx, '-v', 'emu', 'stop', '--all']
-          )
+      RunTestSuiteOnFfxEmu(api, suite, ffx, arch)
 
 
 def BuildFuchsia(api):