Make all delta payloads optional.

This change makes the nton payload optional in addition to the mton
payload which was done in Ib6704594806de33cea38beb968e30304f1529211.

BUG=chromium-os:29567
TEST=unittests + downloader using a lumpy cq build:
unified-lumpy-paladin/R21-2343.0.0-rc8

Change-Id: I7b107b5b055cd78b5210400950c8fa388f6d4f7b
Reviewed-on: https://gerrit.chromium.org/gerrit/23300
Reviewed-by: Chris Sosa <sosa@chromium.org>
Tested-by: Chris Sosa <sosa@chromium.org>
Commit-Ready: Chris Sosa <sosa@chromium.org>
diff --git a/devserver_util.py b/devserver_util.py
index c2a659b..a4633db 100644
--- a/devserver_util.py
+++ b/devserver_util.py
@@ -52,9 +52,9 @@
       else:
         mton_payload_url = payload
 
-  if not full_payload_url or not nton_payload_url:
+  if not full_payload_url:
     raise DevServerUtilError(
-        'Payloads are missing or have unexpected name formats.', payload_list)
+        'Full payload is missing or has unexpected name format.', payload_list)
 
   return full_payload_url, nton_payload_url, mton_payload_url
 
@@ -76,14 +76,17 @@
   full_url, nton_url, mton_url = ParsePayloadList(payload_list)
 
   full_payload = os.path.join(build_dir, downloadable_artifact.ROOT_UPDATE)
-  nton_payload = os.path.join(build_dir, AU_BASE, build + NTON_DIR_SUFFIX,
-                              downloadable_artifact.ROOT_UPDATE)
 
   artifacts = []
   artifacts.append(downloadable_artifact.DownloadableArtifact(full_url,
       main_staging_dir, full_payload, synchronous=True))
-  artifacts.append(downloadable_artifact.AUTestPayload(nton_url,
+
+  if nton_url:
+    nton_payload = os.path.join(build_dir, AU_BASE, build + NTON_DIR_SUFFIX,
+                                downloadable_artifact.ROOT_UPDATE)
+    artifacts.append(downloadable_artifact.AUTestPayload(nton_url,
       main_staging_dir, nton_payload))
+
   if mton_url:
     mton_payload = os.path.join(build_dir, AU_BASE, build + MTON_DIR_SUFFIX,
                                 downloadable_artifact.ROOT_UPDATE)
diff --git a/devserver_util_unittest.py b/devserver_util_unittest.py
index 90f05b3..3864522 100755
--- a/devserver_util_unittest.py
+++ b/devserver_util_unittest.py
@@ -73,6 +73,7 @@
     shutil.rmtree(self._install_dir)
 
   def testParsePayloadList(self):
+    """Tests we can parse the payload list into urls."""
     archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/'
                           'R17-1413.0.0-a1-b1346/')
     mton_url = (archive_url_prefix + 'chromeos_R17-1412.0.0-a1-b1345_'
@@ -99,6 +100,17 @@
     self.assertEqual([full_url, nton_url, mton_url],
                      [full_url_out, nton_url_out, mton_url_out])
 
+  def testParsePayloadListWithoutDeltas(self):
+    """Tests we can parse the payload list when no delta updates exist."""
+    archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/'
+                          'R17-1413.0.0-a1-b1346/')
+    full_url = (archive_url_prefix + 'chromeos_R17-1413.0.0-a1_'
+                'x86-mario_full_dev.bin')
+    full_url_out, nton_url_out, mton_url_out = (
+        devserver_util.ParsePayloadList([full_url, '', '']))
+    self.assertEqual([full_url, None, None],
+                     [full_url_out, nton_url_out, mton_url_out])
+
   def testParsePartialPayloadList(self):
     """Tests that we can parse a payload list with missing optional payload."""
     archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/'
@@ -303,7 +315,6 @@
     for index, artifact in enumerate(artifacts):
       self.assertEqual(artifact._gs_path, expected_payloads[index])
       self.assertTrue(artifact._tmp_staging_dir.startswith(self._static_dir))
-      print 'Will Download Artifact: %s' % artifact
 
     self.mox.VerifyAll()
 
@@ -335,7 +346,37 @@
     for index, artifact in enumerate(artifacts):
       self.assertEqual(artifact._gs_path, expected_payloads[index])
       self.assertTrue(artifact._tmp_staging_dir.startswith(self._static_dir))
-      print 'Will Download Artifact: %s' % artifact
+
+    self.mox.VerifyAll()
+
+  def testGatherArtifactDownloadsWithoutMtonOrNton(self):
+    """Gather the correct download requirements without delta payloads."""
+    build = 'R17-1413.0.0-a1-b1346'
+    archive_url_prefix = ('gs://chromeos-image-archive/x86-mario-release/' +
+                          build)
+    mock_data = 'mock data\nmock_data'
+    payloads = map(lambda x: '/'.join([archive_url_prefix, x]),
+                   ['p1'])
+    expected_payloads = payloads + map(
+        lambda x: '/'.join([archive_url_prefix, x]),
+            [downloadable_artifact.STATEFUL_UPDATE,
+             downloadable_artifact.AUTOTEST_PACKAGE,
+             downloadable_artifact.TEST_SUITES_PACKAGE])
+    self.mox.StubOutWithMock(gsutil_util, 'GSUtilRun')
+    self.mox.StubOutWithMock(devserver_util, 'ParsePayloadList')
+
+    # GSUtil ls.
+    gsutil_util.GSUtilRun(mox.StrContains(archive_url_prefix),
+                          mox.IgnoreArg()).AndReturn(mock_data)
+    devserver_util.ParsePayloadList(mock_data.splitlines()).AndReturn(
+        payloads + [None, None])
+
+    self.mox.ReplayAll()
+    artifacts = devserver_util.GatherArtifactDownloads(
+        self._static_dir, archive_url_prefix, build, self._install_dir)
+    for index, artifact in enumerate(artifacts):
+      self.assertEqual(artifact._gs_path, expected_payloads[index])
+      self.assertTrue(artifact._tmp_staging_dir.startswith(self._static_dir))
 
     self.mox.VerifyAll()