Update devserver to support downloader other than from Google Storage

Main changes:
1. Restructure artifact wrappers to support both CrOS and Android artifacts.
2. Support different downloaders in devserver.py.
3. Add LaunchControlDownloader class, the functions are to be implemented.

BUG=chromium:512668
TEST=run_unittests, devserver_integration_test.py, guado_moblab (au and dummy)
cros flash and cros stage to guado moblab

Change-Id: Ia350b00a2a5ceaeff6d922600dc84c8fc7295ef9
Reviewed-on: https://chromium-review.googlesource.com/301992
Commit-Ready: Dan Shi <dshi@chromium.org>
Tested-by: Dan Shi <dshi@chromium.org>
Reviewed-by: Dan Shi <dshi@chromium.org>
diff --git a/downloader_unittest.py b/downloader_unittest.py
index 64ca12e..16909da 100755
--- a/downloader_unittest.py
+++ b/downloader_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python2
 #
 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
@@ -6,6 +6,8 @@
 
 """Unit tests for downloader module."""
 
+from __future__ import print_function
+
 import mox
 import os
 import shutil
@@ -32,26 +34,26 @@
   def tearDown(self):
     shutil.rmtree(self._work_dir, ignore_errors=True)
 
-  def _SimpleDownloadOfTestSuites(self, archive_path):
+  def _SimpleDownloadOfTestSuites(self, downloader_instance):
     """Helper to verify test_suites are downloaded correctly.
 
     Args:
-      archive_path: Archive url or local path to test with.
+      downloader_instance: Downloader object to test with.
     """
-    downloader_instance = downloader.Downloader(self._work_dir,
-                                                archive_path)
+    factory = build_artifact.ChromeOSArtifactFactory(
+        downloader_instance.GetBuildDir(), ['test_suites'],
+        None, downloader_instance.GetBuild())
     self.mox.StubOutWithMock(downloader.Downloader,
                              '_DownloadArtifactsSerially')
     self.mox.StubOutWithMock(downloader.Downloader,
                              '_DownloadArtifactsInBackground')
 
     downloader.Downloader._DownloadArtifactsInBackground(mox.In(mox.IsA(
-        build_artifact.AutotestTarballBuildArtifact)))
+        build_artifact.AutotestTarball)))
     downloader.Downloader._DownloadArtifactsSerially(
-        [mox.IsA(build_artifact.BundledBuildArtifact)], no_wait=True)
+        [mox.IsA(build_artifact.BundledArtifact)], no_wait=True)
     self.mox.ReplayAll()
-    downloader_instance.Download(artifacts=['test_suites'],
-                                 files=None)
+    downloader_instance.Download(factory)
     # Sanity check the timestamp file exists.
     self.assertTrue(os.path.exists(
         os.path.join(self._work_dir, self.board, self.build,
@@ -64,7 +66,8 @@
     Verifies that if we request the test_suites from Google Storage, it gets
     downloaded and the autotest tarball is attempted in the background.
     """
-    self._SimpleDownloadOfTestSuites(self.archive_url)
+    self._SimpleDownloadOfTestSuites(
+        downloader.GoogleStorageDownloader(self._work_dir, self.archive_url))
 
   def testSimpleDownloadOfTestSuitesFromLocal(self):
     """Basic test_suites test.
@@ -72,30 +75,74 @@
     Verifies that if we request the test_suites from a local path, it gets
     downloaded and the autotest tarball is attempted in the background.
     """
-    self._SimpleDownloadOfTestSuites(self.local_path)
+    self._SimpleDownloadOfTestSuites(
+        downloader.LocalDownloader(self._work_dir, self.local_path))
 
-  def _DownloadSymbolsHelper(self, archive_path):
+  def _DownloadSymbolsHelper(self, downloader_instance):
     """Basic symbols download."""
-    downloader_instance = downloader.Downloader(self._work_dir, archive_path)
+    factory = build_artifact.ChromeOSArtifactFactory(
+        downloader_instance.GetBuildDir(), ['symbols'],
+        None, downloader_instance.GetBuild())
+
     self.mox.StubOutWithMock(downloader.Downloader,
                              '_DownloadArtifactsSerially')
     # Should not get called but mocking so that we know it wasn't called.
     self.mox.StubOutWithMock(downloader.Downloader,
                              '_DownloadArtifactsInBackground')
     downloader.Downloader._DownloadArtifactsSerially(
-        [mox.IsA(build_artifact.BundledBuildArtifact)], no_wait=True)
+        [mox.IsA(build_artifact.BundledArtifact)], no_wait=True)
     self.mox.ReplayAll()
-    downloader_instance.Download(artifacts=['symbols'],
-                                 files=None)
+    downloader_instance.Download(factory)
     self.mox.VerifyAll()
 
   def testDownloadSymbolsFromGS(self):
     """Basic symbols download from Google Storage."""
-    self._DownloadSymbolsHelper(self.archive_url)
+    self._DownloadSymbolsHelper(
+        downloader.GoogleStorageDownloader(self._work_dir, self.archive_url))
 
   def testDownloadSymbolsFromLocal(self):
     """Basic symbols download from a Local Path."""
-    self._DownloadSymbolsHelper(self.local_path)
+    self._DownloadSymbolsHelper(
+        downloader.LocalDownloader(self._work_dir, self.local_path))
+
+
+class AndroidDownloaderTestBase(mox.MoxTestBase):
+  """Android Downloader Unittests."""
+
+  def setUp(self):
+    mox.MoxTestBase.setUp(self)
+    self._work_dir = tempfile.mkdtemp('downloader-test')
+    self.target = 'shamu-userdebug'
+    self.build_id = '123456'
+
+  def tearDown(self):
+    shutil.rmtree(self._work_dir, ignore_errors=True)
+
+  def testDownloadFromLaunchControl(self):
+    """Basic test to check download from LaunchControl works."""
+    downloader_instance = downloader.LaunchControlDownloader(
+        self._work_dir, self.build_id, self.target)
+    factory = build_artifact.AndroidArtifactFactory(
+        downloader_instance.GetBuildDir(), ['fastboot'],
+        None, downloader_instance.GetBuild())
+    self.mox.StubOutWithMock(downloader.Downloader,
+                             '_DownloadArtifactsSerially')
+    self.mox.StubOutWithMock(downloader.Downloader,
+                             '_DownloadArtifactsInBackground')
+
+    # TODO(dshi): Uncomment following line after Fetch method is implemented in
+    # LaunchControlDownloader.
+    # downloader.Downloader._DownloadArtifactsInBackground(mox.In(mox.IsA(
+    #     build_artifact.ANDROID_FASTBOOT)))
+    downloader.Downloader._DownloadArtifactsSerially(
+        [mox.IsA(build_artifact.Artifact)], no_wait=True)
+    self.mox.ReplayAll()
+    downloader_instance.Download(factory)
+    # Sanity check the timestamp file exists.
+    self.assertTrue(os.path.exists(
+        os.path.join(self._work_dir, self.target, self.build_id,
+                     downloader.Downloader._TIMESTAMP_FILENAME)))
+    self.mox.VerifyAll()
 
 
 if __name__ == '__main__':