text: from os.path import normpath
  import pytest
  import requests
  from requests.exceptions import RequestException
  
  from coursera.workflow import CourseraDownloader, _iter_modules, _walk_modules
  from coursera.commandline import parse_args
  from coursera.parallel import ConsecutiveDownloader, ParallelDownloader
  from coursera.downloaders import Downloader
  
  
  class MockedCommandLineArgs(object):
      """
      This mock uses default arguments from parse_args and allows to overwrite
      them in constructor.
      """
      def __init__(self, **kwargs):
          args = parse_args('-u username -p password test_class'.split())
          self.__dict__.update(args.__dict__)
          self.__dict__.update(kwargs)
  
      def __repr__(self):
          return self.__dict__.__repr__()
  
  
  class MockedFailingDownloader(Downloader):
      """
      This mock will raise whatever exception you pass to it in constructor
      in _start_download method. Pass None to prevent any exception.
      """
      def __init__(self, exception_to_throw):
          self._exception_to_throw = exception_to_throw
  
      def _start_download(self, *args, **kwargs):
          if self._exception_to_throw is None:
              return
          raise self._exception_to_throw
  
  
  TEST_URL = "https://api.coursera.org/api/test-url"
  
  
  def make_test_modules():
      modules = [
          ["section1",
           [
               ["module1",
                [
                    ["lecture1",
                     {"en.txt": [
                         [TEST_URL,
                          "title"
                         ]
                     ]
                     }
                    ]
                ]
               ]
           ]
          ]
      ]
      return modules
  
  
  @pytest.mark.parametrize(
      'expected_failed_urls,exception_to_throw,downloader_wrapper_class', [
          ([], None, ConsecutiveDownloader),
          ([], None, ParallelDownloader),
          ([TEST_URL], RequestException('Test exception'), ConsecutiveDownloader),
          ([TEST_URL], RequestException('Test exception'), ParallelDownloader),
          ([TEST_URL], Exception('Test exception'), ConsecutiveDownloader),
          ([TEST_URL], Exception('Test exception'), ParallelDownloader),
          ([TEST_URL], ValueError('Test exception'), ConsecutiveDownloader),
          ([TEST_URL], ValueError('Test exception'), ParallelDownloader),
          ([TEST_URL], AttributeError('Test exception'), ConsecutiveDownloader),
          ([TEST_URL], AttributeError('Test exception'), ParallelDownloader),
      ]
  )
  def test_failed_urls_are_collected(expected_failed_urls,
                                     exception_to_throw,
                                     downloader_wrapper_class):
      """
      This test makes sure that if there was an exception in the file downloader,
      downloader wrapper will intercept it and course downloader will record
      the problematic URL.
      """
      file_downloader = MockedFailingDownloader(exception_to_throw)
      course_downloader = CourseraDownloader(
          downloader=downloader_wrapper_class(file_downloader),
          commandline_args=MockedCommandLineArgs(overwrite=True),
          class_name='test_class',
          path='',
          ignored_formats=None,
          disable_url_skipping=False)
      modules = make_test_modules()
  
      course_downloader.download_modules(modules)
      assert expected_failed_urls == course_downloader.failed_urls
  
  
  def test_iter_modules():
      """
      Test that all modules are iterated and intermediate values are formatted
      correctly. Filtering is not tested at the moment.
      """
      modules = make_test_modules()
      args = MockedCommandLineArgs()
  
      expected_output = [
          (0, '01_section1'),
          (0, normpath('test_class/01_section1/01_module1')),
          (0, 'lecture1', 'en.txt', 'title'),
          ('en.txt', 'https://api.coursera.org/api/test-url', 'title')
      ]
      collected_output = []
  
      for module in _iter_modules(modules=modules, class_name='test_class',
                                  path='', ignored_formats=None, args=args):
          collected_output.append((module.index, module.name))
          for section in module.sections:
              collected_output.append((section.index, section.dir))
              for lecture in section.lectures:
                  for resource in lecture.resources:
                      collected_output.append((lecture.index, lecture.name,
                                               resource.fmt, resource.title))
                      collected_output.append((resource.fmt, resource.url, resource.title))
  
      assert expected_output == collected_output
  
  def test_walk_modules():
      """
      Test _walk_modules, a flattened version of _iter_modules.
      """
      modules = make_test_modules()
      args = MockedCommandLineArgs()
  
      expected_output = [
          (0, '01_section1',
           0, normpath('test_class/01_section1/01_module1'),
           0, 'lecture1', normpath('test_class/01_section1/01_module1/01_lecture1_title.en.txt'),
           'https://api.coursera.org/api/test-url')]
      collected_output = []
  
      for module, section, lecture, resource in _walk_modules(
              modules=modules, class_name='test_class',
              path='', ignored_formats=None, args=args):
  
          collected_output.append(
              (module.index, module.name,
               section.index, section.dir,
               lecture.index, lecture.name, lecture.filename(resource.fmt, resource.title),
               resource.url)
          )
  
      assert expected_output == collected_output
  