Repository: google/clusterfuzz

Issue:
Auto-find nearest revision to a provided upload revision if it does not exist.



Discussion:

Resolving patch:
diff --git a/src/python/bot/tasks/analyze_task.py b/src/python/bot/tasks/analyze_task.py
--- a/src/python/bot/tasks/analyze_task.py
+++ b/src/python/bot/tasks/analyze_task.py
@@ -18,6 +18,7 @@
 import datetime
 import six
 
+from base import errors
 from base import tasks
 from base import utils
 from bot import testcase_manager
@@ -25,6 +26,7 @@
 from bot.tasks import setup
 from bot.tasks import task_creation
 from build_management import build_manager
+from build_management import revisions
 from chrome import crash_uploader
 from crash_analysis import crash_analyzer
 from crash_analysis import severity_analyzer
@@ -79,6 +81,28 @@ def close_invalid_testcase_and_update_status(testcase, metadata, status):
   metadata.put()
 
 
+def setup_build(testcase):
+  """Set up a custom or regular build based on revision. For regular builds,
+  if a provided revision is not found, set up a build with the
+  closest revision <= provided revision."""
+  revision = testcase.crash_revision
+
+  if revision and not build_manager.is_custom_binary():
+    build_bucket_path = build_manager.get_primary_bucket_path()
+    revision_list = build_manager.get_revisions_list(
+        build_bucket_path, testcase=testcase)
+    if not revision_list:
+      logs.log_error('Failed to fetch revision list.')
+      return
+
+    revision_index = revisions.find_min_revision_index(revision_list, revision)
+    if revision_index is None:
+      raise errors.BuildNotFoundError(revision, testcase.job_type)
+    revision = revision_list[revision_index]
+
+  build_manager.setup_build(revision)
+
+
 def execute_task(testcase_id, job_type):
   """Run analyze task."""
   # Reset redzones.
@@ -121,13 +145,13 @@ def execute_task(testcase_id, job_type):
   if metadata.retries is not None:
     environment.set_value('CRASH_RETRIES', metadata.retries)
 
-  # Setup testcase and get absolute testcase path.
+  # Set up testcase and get absolute testcase path.
   file_list, _, testcase_file_path = setup.setup_testcase(testcase, job_type)
   if not file_list:
     return
 
-  # Set up a custom or regular build based on revision.
-  build_manager.setup_build(testcase.crash_revision)
+  # Set up build.
+  setup_build(testcase)
 
   # Check if we have an application path. If not, our build failed
   # to setup correctly.


Test patch:
diff --git a/src/python/tests/core/build_management/revisions_test.py b/src/python/tests/core/build_management/revisions_test.py
--- a/src/python/tests/core/build_management/revisions_test.py
+++ b/src/python/tests/core/build_management/revisions_test.py
@@ -347,6 +347,30 @@ def test_get_component_range_list_custom_binary_job(
                      revisions.get_component_range_list(1337, 1338,
                                                         CUSTOM_BINARY_JOB_TYPE))
 
+  def test_find_build_url(self):
+    """Tests test_find_build_url."""
+    bucket_path = 'gs://bucket/path/asan-([0-9]+).zip'
+    build_urls_list = [
+        'gs://bucket/path/asan-1000.zip',
+        'gs://bucket/path/asan-2000.zip',
+        'gs://bucket/path/asan-3000.zip',
+        'gs://bucket/path/asan-4000.zip',
+    ]
+    self.assertEqual(
+        revisions.find_build_url(bucket_path, build_urls_list, 1000),
+        'gs://bucket/path/asan-1000.zip')
+    self.assertEqual(
+        revisions.find_build_url(bucket_path, build_urls_list, 2000),
+        'gs://bucket/path/asan-2000.zip')
+    self.assertEqual(
+        revisions.find_build_url(bucket_path, build_urls_list, 3000),
+        'gs://bucket/path/asan-3000.zip')
+    self.assertEqual(
+        revisions.find_build_url(bucket_path, build_urls_list, 4000),
+        'gs://bucket/path/asan-4000.zip')
+    self.assertIsNone(
+        revisions.find_build_url(bucket_path, build_urls_list, 5000))
+
   def test_find_min_revision_index(self):
     """Tests find_min_revision_index()."""
     revisions_list = [1000, 2000, 3000, 4000]