pw_presubmit: Handle empty list of steps

It's possible that a program doesn't contain any steps that apply to the
files modified by a commit. This should result in a passing build, not a
failing build.

A build with empty values for both the program and step properties will
still fail—this only covers when there is a program property but it
doesn't expand to any steps.

Change-Id: I42408053f9bdcc4509d85cef4f724049abc30d6e
Bug: 588
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/77060
Pigweed-Auto-Submit: Rob Mohr <mohrr@google.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/recipe_modules/pw_presubmit/api.py b/recipe_modules/pw_presubmit/api.py
index efc8323..0994e45 100644
--- a/recipe_modules/pw_presubmit/api.py
+++ b/recipe_modules/pw_presubmit/api.py
@@ -48,6 +48,7 @@
         self._root = None
         self._checkout_root = None
         self._step_objects = None
+        self._initialized = False
 
     @property
     def command_name(self):
@@ -73,6 +74,8 @@
         return self._input_steps or self._input_programs
 
     def init(self, checkout_root):
+        self._initialized = True
+
         if not self._input_steps and not self._input_programs:
             raise self.m.step.StepFailure('no step or program properties')
 
@@ -108,14 +111,11 @@
                     for step_name in program_steps:
                         self._step_objects[step_name] = self._step(step_name)
 
-        if not self._step_objects:
-            raise self.m.step.StepFailure('no steps to execute')
-
     def steps(self):
         # We shouldn't get to here, but in case the caller doesn't call init()
         # first we'll check anyway.
-        if not self._step_objects:  # pragma: no cover
-            raise self.m.step.StepFailure('no steps to execute')
+        if not self._initialized:  # pragma: no cover
+            raise self.m.step.StepFailure('api.pw_presubmit.init() not called')
 
         return self._step_objects.values()
 
diff --git a/recipe_modules/pw_presubmit/tests/full.expected/empty-program.json b/recipe_modules/pw_presubmit/tests/full.expected/empty-program.json
index e0023f1..b7aa75b 100644
--- a/recipe_modules/pw_presubmit/tests/full.expected/empty-program.json
+++ b/recipe_modules/pw_presubmit/tests/full.expected/empty-program.json
@@ -28,10 +28,6 @@
     ]
   },
   {
-    "failure": {
-      "failure": {},
-      "humanReason": "no steps to execute"
-    },
     "name": "$result"
   }
 ]
\ No newline at end of file
diff --git a/recipe_modules/pw_presubmit/tests/full.py b/recipe_modules/pw_presubmit/tests/full.py
index b062758..3688602 100644
--- a/recipe_modules/pw_presubmit/tests/full.py
+++ b/recipe_modules/pw_presubmit/tests/full.py
@@ -63,7 +63,7 @@
     )
 
     yield (
-        api.status_check.test('empty-program', status='failure')
+        api.status_check.test('empty-program')
         + properties(program=['empty'])
         + api.step_data(
             "get steps from programs.empty", stdout=api.raw_io.output_text(''),