buildframework/helium/tools/common/python/lib/cpythontest/test_ats4_aste.py
changeset 179 d8ac696cc51f
equal deleted inserted replaced
1:be27ed110b50 179:d8ac696cc51f
       
     1 # -*- coding: latin-1 -*-
       
     2 
       
     3 #============================================================================ 
       
     4 #Name        : test_ats4_aste.py 
       
     5 #Part of     : Helium 
       
     6 
       
     7 #Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     8 #All rights reserved.
       
     9 #This component and the accompanying materials are made available
       
    10 #under the terms of the License "Eclipse Public License v1.0"
       
    11 #which accompanies this distribution, and is available
       
    12 #at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
    13 #
       
    14 #Initial Contributors:
       
    15 #Nokia Corporation - initial contribution.
       
    16 #
       
    17 #Contributors:
       
    18 #
       
    19 #Description:
       
    20 #===============================================================================
       
    21 
       
    22 """ Testing ATS4 ASTE framework. """
       
    23 
       
    24 # pylint: disable-msg=E1101
       
    25 
       
    26 from cStringIO import StringIO
       
    27 from pprint import pprint
       
    28 from xml.etree.ElementTree import fromstring, tostring
       
    29 from xml.etree import ElementTree as et
       
    30 import difflib
       
    31 import logging
       
    32 logging.getLogger().setLevel(logging.ERROR)
       
    33 import re
       
    34 import tempfile
       
    35 import zipfile
       
    36 import os
       
    37 
       
    38 from path import path
       
    39 import amara
       
    40 import mocker
       
    41 
       
    42 import ats3.aste
       
    43 
       
    44 TEST_PATH = None
       
    45 TEST_FILES = {}
       
    46 TEST_ASSET_FILES = {}
       
    47 TSRC = None
       
    48 OUTPUT = None
       
    49 TEST_ZIP_PATH = None
       
    50 
       
    51 # Shortcuts
       
    52 E = et.Element
       
    53 SE = et.SubElement
       
    54 
       
    55 _logger = logging.getLogger("test.ats4_aste")
       
    56 
       
    57 class Bunch(object):
       
    58     
       
    59     def __init__(self, **kwargs): 
       
    60         self.__dict__.update(kwargs)
       
    61     
       
    62 
       
    63 def equal_xml(xml1, xml2):
       
    64     """Check the equality of the given XML snippets.
       
    65     
       
    66     Tag name equality:
       
    67     
       
    68     >>> equal_xml('<a/>', '<a/>')
       
    69     True
       
    70     >>> equal_xml('<a/>', '<b/>')
       
    71     False
       
    72     
       
    73     Attribute equality:
       
    74     
       
    75     >>> equal_xml('<a k="v"/>', '<a k="v"/>')
       
    76     True
       
    77     >>> equal_xml('<a k="v"/>', '<a k="w"/>')
       
    78     False
       
    79     
       
    80     Text content equality:
       
    81     
       
    82     >>> equal_xml('<a>v</a>', '<a>v</a>')
       
    83     True
       
    84     >>> equal_xml('<a>v</a>', '<a>w</a>')
       
    85     False
       
    86     >>> equal_xml('<a>v</a>', '<a></a>')
       
    87     False
       
    88     
       
    89     Text content equality when whitespace differs:
       
    90     >>> equal_xml('<a>v</a>', '<a>v </a>')
       
    91     True
       
    92 
       
    93     Equality of child elements:
       
    94     
       
    95     >>> equal_xml('<a><b><c k="v"/></b></a>', '<a><b><c k="v"/></b></a>')
       
    96     True
       
    97     >>> equal_xml('<a><b><c k="v"/></b></a>', '<a><b><c k="w"/></b></a>')
       
    98     False
       
    99     >>> equal_xml('<a><b><c k="v"/>v</b></a>', '<a><b><c k="v"/>w</b></a>')
       
   100     False
       
   101     >>> equal_xml('<a><b><c k="v"/>v</b></a>', '<a><b><c k="v"/>v </b></a>')
       
   102     True
       
   103     
       
   104     """
       
   105     if isinstance(xml1, basestring):
       
   106         xml1 = fromstring(xml1)
       
   107     if isinstance(xml2, basestring):
       
   108         xml2 = fromstring(xml2)
       
   109     if xml1.tag != xml2.tag:
       
   110         return False
       
   111     if xml1.attrib != xml2.attrib:
       
   112         return False
       
   113     if xml1.text:
       
   114         if not xml2.text:
       
   115             return False
       
   116     if xml2.text:
       
   117         if not xml1.text:
       
   118             return False
       
   119     if xml1.text and xml2.text and xml1.text.strip() != xml2.text.strip():
       
   120         return False
       
   121     if xml1.tail is not None and xml2.tail is not None:
       
   122         if xml1.tail.strip() != xml2.tail.strip():
       
   123             return False
       
   124     elif xml1.tail != xml2.tail:
       
   125         return False
       
   126     children1 = list(xml1.getchildren())
       
   127     children2 = list(xml2.getchildren())
       
   128     if len(children1) != len(children2):
       
   129         return False
       
   130     for child1, child2 in zip(children1, children2):
       
   131         return equal_xml(child1, child2)
       
   132     return True        
       
   133 
       
   134 
       
   135 def setup_module():
       
   136     global TEST_PATH, OUTPUT, TEST_ZIP_PATH
       
   137     TEST_PATH = path(tempfile.mkdtemp())
       
   138     OUTPUT = TEST_PATH.joinpath("TestAsset")
       
   139     TEST_ZIP_PATH = TEST_PATH.joinpath("test_zip")
       
   140     asset = TEST_PATH
       
   141     component = TEST_PATH
       
   142     component.joinpath("group").makedirs()
       
   143     for path_parts in (("output", "images", "file1.fpsx"),
       
   144                        ("output", "images", "file2.fpsx")):
       
   145         filename = component.joinpath(*path_parts)
       
   146         if not filename.parent.exists():
       
   147             filename.parent.makedirs()
       
   148         filename.touch()
       
   149         TEST_FILES.setdefault(path_parts[1], []).append(file)
       
   150     for path_parts in (("TestAsset", "Localisation", "S60", "localisation.txt"),
       
   151                        ("TestAsset", "TestCases", "TC_100_Test0", "file1.sis"),
       
   152                        ("TestAsset", "TestCases", "TC_100_Test0", "file2.tcf"),
       
   153                        ("TestAsset", "Tools", "TestCaseCreator", "test_creator.ini"),
       
   154                        ("TestAsset", "testdrop.xml"),):
       
   155         filename = asset.joinpath(*path_parts)
       
   156         if not filename.parent.exists():
       
   157             filename.parent.makedirs()
       
   158         filename.touch()
       
   159         TEST_ASSET_FILES.setdefault(path_parts[1], []).append(file)
       
   160     try:
       
   161         zip_component = TEST_ZIP_PATH
       
   162         filename = zip_component.joinpath("TestAsset.zip")
       
   163         if not filename.parent.exists():
       
   164             filename.parent.makedirs()
       
   165         filename.touch()
       
   166         zfile = zipfile.ZipFile(zip_component.joinpath("TestAsset.zip"), "w", zipfile.ZIP_DEFLATED)
       
   167         for p in TEST_ASSET_FILES:
       
   168             print p
       
   169             zfile.write(p)
       
   170         zfile.close()
       
   171         TEST_ASSET_FILES.setdefault("ZIP", []).append(file)
       
   172     except OSError:
       
   173         print "Got except OSError. Continuing...\n"  
       
   174         
       
   175     
       
   176 def teardown_module():
       
   177     path(TEST_PATH).rmtree()
       
   178     
       
   179     
       
   180 class TestTestPlan(mocker.MockerTestCase):
       
   181 
       
   182     def __init__(self, methodName="runTest"):
       
   183         mocker.MockerTestCase.__init__(self, methodName)
       
   184              
       
   185     def setUp(self):
       
   186         opts = Bunch(testrun_name="testrun", harness="ASTE", 
       
   187                      device_type="product", plan_name="ats3_test_plan", diamonds_build_url="",
       
   188                      software_version="W810", software_release="SPP 51.32", device_language="English",
       
   189                      testasset_location=TEST_PATH.joinpath("TestAsset"), testasset_caseids="100",repeat="1", report_email="",
       
   190                      file_store=path(), test_timeout="60", device_hwid="5425", test_type="smoke")
       
   191         self.tp = ats3.aste.AsteTestPlan(opts)
       
   192         self.image_files = TEST_FILES["images"]
       
   193         self.test_timeout = self.tp["test_timeout"]
       
   194         self.device_hwid = self.tp["device_hwid"]
       
   195         self.test_harness = self.tp["harness"]
       
   196         self.device_language = self.tp["device_language"]
       
   197         self.software_release = self.tp["software_release"]
       
   198         self.software_version = self.tp["software_version"]
       
   199         self.testasset_caseids = self.tp["testasset_caseids"]
       
   200         self.testasset_location = self.tp["testasset_location"]
       
   201         self.test_type = self.tp["test_type"]
       
   202         
       
   203         if self.testasset_location != "":
       
   204             self.test_asset_testcases = [self.testasset_location.joinpath("TestCases", "TC_100_Test0", "file1.sis"), self.testasset_location.joinpath("TestCases", "TC_100_Test0", "file2.tcf")]
       
   205             self.test_asset_tools = [self.testasset_location.joinpath("Tools", "TestCaseCreator", "test_creator.ini")]
       
   206             self.test_asset_localisation = [self.testasset_location.joinpath("Localisation", "S60", "localisation.txt")]
       
   207             self.test_asset_testdrop = self.testasset_location.joinpath("testdrop.xml")
       
   208         else:
       
   209             self.test_asset_testcases = TEST_ASSET_FILES["TestCases"]
       
   210             self.test_asset_tools = TEST_ASSET_FILES["Tools"]
       
   211             self.test_asset_localisation = TEST_ASSET_FILES["Localisation"]
       
   212             self.test_asset_testdrop = TEST_ASSET_FILES["testdrop.xml"]
       
   213 
       
   214             
       
   215     def test_creation(self):        
       
   216         assert self.tp["testrun_name"] == "testrun"
       
   217         assert self.tp["harness"] == "ASTE"
       
   218         assert self.tp["device_type"] == "product"
       
   219     
       
   220     def test_insert_set(self):
       
   221         
       
   222         self.tp.insert_set(image_files=self.image_files,
       
   223                            test_timeout=self.test_timeout)
       
   224         
       
   225         assert self.tp.sets[0] == dict(name="set0",
       
   226                                        image_files=self.image_files,
       
   227                                        test_timeout=self.test_timeout,
       
   228                                        test_harness=self.test_harness)
       
   229 
       
   230     def test_post_actions_email(self):
       
   231         assert not self.tp.post_actions
       
   232         receiver = "joe.average@example.com"
       
   233         self.tp.report_email = receiver
       
   234         assert len(self.tp.post_actions) == 1
       
   235         action, items = self.tp.post_actions[0]
       
   236         items = dict(items)
       
   237         assert action == "SendEmailAction"
       
   238         assert items["to"] == receiver
       
   239     
       
   240     def test_post_actions_ats3_report_only(self):
       
   241         file_store = path("path/to/files")
       
   242         self.tp.file_store = file_store
       
   243         self.tp.harness = "EUNIT"
       
   244         assert len(self.tp.post_actions) == 2
       
   245         action, items = self.tp.post_actions[0]
       
   246         items = dict(items)
       
   247         assert action == "FileStoreAction"
       
   248         assert items["report-type"] == "ATS_REPORT"
       
   249         assert items["to-folder"].startswith(file_store)
       
   250         assert items["to-folder"].endswith("ATS3_REPORT")
       
   251     
       
   252     def test_post_actions_aste(self):
       
   253         file_store = path("path/to/files")
       
   254         self.tp.file_store = file_store
       
   255         assert len(self.tp.post_actions) == 2
       
   256         action, items = self.tp.post_actions[1]
       
   257         items = dict(items)
       
   258         assert action == "FileStoreAction"
       
   259         assert items["report-type"] == "ASTE_REPORT"
       
   260         assert items["to-folder"].startswith(file_store)
       
   261         assert items["to-folder"].endswith("ASTE_REPORT")
       
   262         
       
   263     def test_post_actions_diamonds(self):
       
   264         self.tp.diamonds_build_url = "http://diamonds.nmp.company.com/diamonds/builds/1234"
       
   265         assert len(self.tp.post_actions) == 1
       
   266         action, items = self.tp.post_actions[0]
       
   267         assert action == "DiamondsAction"
       
   268         assert not items
       
   269 
       
   270 
       
   271             
       
   272 class TestXMLGeneration(mocker.MockerTestCase):
       
   273     """
       
   274     Unit tests for the test.xml generation.
       
   275     """    
       
   276 
       
   277     def __init__(self, methodName="runTest"):
       
   278         self.image_files = None
       
   279         self.report_email = None
       
   280         self.diamonds_build_url = None
       
   281         self.test_harness = None
       
   282         self.file_store = None
       
   283         self.testasset_location = None
       
   284         self.test_plan = None
       
   285         self.gen = None
       
   286         mocker.MockerTestCase.__init__(self, methodName)
       
   287         
       
   288         
       
   289     def generate_xml(self):
       
   290         def files(*paths):
       
   291             return [TEST_PATH.joinpath(p) for p in paths]
       
   292         self.image_files = files("output/images/file1.fpsx", "output/images/file2.fpsx")
       
   293         self.report_email = "test.receiver@company.com"
       
   294         self.diamonds_build_url = "http://diamonds.nmp.company.com/diamonds/builds/1234"
       
   295         self.test_harness = "ASTE"
       
   296         self.file_store = path(r"path/to/reports")
       
   297         self.testasset_location = OUTPUT
       
   298         
       
   299         self.mocker.restore()
       
   300         test_plan = self.mocker.mock(count=False)
       
   301         mocker.expect(test_plan["testrun_name"]).result("test")
       
   302         mocker.expect(test_plan["harness"]).result("ASTE")
       
   303         mocker.expect(test_plan["device_type"]).result("product")
       
   304         mocker.expect(test_plan["plan_name"]).result("test plan")
       
   305         mocker.expect(test_plan["diamonds_build_url"]).result(self.diamonds_build_url)
       
   306         mocker.expect(test_plan["test_timeout"]).result("60")
       
   307         mocker.expect(test_plan["device_hwid"]).result("5425")
       
   308         mocker.expect(test_plan["testasset_location"]).result(self.testasset_location)
       
   309         mocker.expect(test_plan["testasset_caseids"]).result("100")
       
   310         mocker.expect(test_plan["report_email"]).result(self.report_email)
       
   311         mocker.expect(test_plan["software_release"]).result("SPP 51.32")
       
   312         mocker.expect(test_plan["software_version"]).result("W810")
       
   313         mocker.expect(test_plan["device_language"]).result("English")
       
   314         mocker.expect(test_plan["test_type"]).result("smoke")
       
   315         mocker.expect(test_plan["temp_directory"]).result(TEST_PATH)
       
   316         mocker.expect(test_plan.sets).result([
       
   317             dict(name="set0", image_files=self.image_files, test_harness="ASTE")])
       
   318         mocker.expect(test_plan.post_actions).result([
       
   319             ("EmailAction", (("subject", "Release testing"),
       
   320                                  ("to", self.report_email))),
       
   321 #            ("FileStoreAction", (("to-folder", self.file_store),
       
   322 #                                 ("report-type", "ATS_REPORT"),
       
   323 #                                 ("date-format", "yyyyMMdd"),
       
   324 #                                 ("time-format", "HHmmss"))),
       
   325 #            ("FileStoreAction", (("to-folder", self.file_store),
       
   326 #                                 ("report-type", "ASTE_REPORT"),
       
   327 #                                 ("run-log", "true"),
       
   328 #                                 ("date-format", "yyyyMMdd"),
       
   329 #                                 ("time-format", "HHmmss"))),
       
   330             ("DiamondsAction", ())
       
   331         ])
       
   332         
       
   333         self.mocker.replay()
       
   334         self.test_plan = test_plan
       
   335         
       
   336         self.gen = ats3.aste.AsteTemplateTestDropGenerator()
       
   337         return self.gen.generate_xml(test_plan)
       
   338 
       
   339     def test_basic_structure(self):
       
   340         "Check that the overall test.xml structure is valid."
       
   341         xml = self.generate_xml()
       
   342         # Check basics.
       
   343 #        assert xml.find(".").tag == "test"
       
   344 #        assert xml.find("./name").text == "test"
       
   345 #        assert xml.find("./buildid").text == self.diamonds_build_url
       
   346 #        assert xml.find("./target").tag
       
   347 #        assert xml.find("./target/device").tag
       
   348 #        harness, hardware, device_hwid = xml.findall("./target/device/property")
       
   349 #        softwareVersion, softwareRelease, deviceLanguage = xml.findall("./target/device/setting")
       
   350 #        assert harness.get("value") == "ASTE"
       
   351 #        assert hardware.get("value") == "product"
       
   352 #        assert softwareVersion.get("value") == "W810"
       
   353 #        assert softwareRelease.get("value") == "SPP 51.32"
       
   354 #        assert deviceLanguage.get("value") == "English"
       
   355 #        assert device_hwid.get("value") == "5425"
       
   356 #        
       
   357 #        # Check generation of the test plan.
       
   358 #        assert xml.find("./plan").get("name") == "Plan smoke product"
       
   359 #        assert xml.find("./plan/session").tag 
       
   360 #        sets = xml.findall("./plan/session/set")
       
   361 #        assert len(sets) == 1
       
   362 #        assert sets[0].get("name") == "set0"
       
   363 #        assert sets[0].find("./target/device").tag
       
   364     
       
   365     def test_set_structure(self):
       
   366         "Check that a <set> element's structure is valid."
       
   367         xml = self.generate_xml()
       
   368 #        tstset = xml.find("./plan/session/set")
       
   369 #        assert tstset.tag
       
   370 #        case = tstset.find("./case")
       
   371 #        assert case.tag
       
   372 #        assert case.get("name") == "set0 case"                
       
   373         
       
   374     def test_case_flash_elems(self):
       
   375         """ Test case flash elems. """
       
   376         xml = self.generate_xml()
       
   377         found = False
       
   378         for case in xml.findall(".//task"):
       
   379             if case.find('type').text == 'FlashTask':
       
   380                 found = True
       
   381                 flashes = case.findall("./parameters/parameter")
       
   382                 assert len(flashes) == len(self.image_files)
       
   383                 for i, flash_file in enumerate(self.image_files):
       
   384                     assert flashes[i].get("name") == "image-" + str(i + 1)
       
   385                     assert flashes[i].get("value") == "ATS3Drop\\images\\" + flash_file.name
       
   386         assert found
       
   387     
       
   388     def test_steps(self):
       
   389         xml = self.generate_xml()
       
   390         steps = iter(xml.findall(".//task"))
       
   391         step = steps.next()
       
   392         step = steps.next()
       
   393         self.check_executeasset_step(steps)
       
   394 
       
   395     def check_executeasset_step(self, steps):
       
   396         step = steps.next()
       
   397         assert step.findtext("./type") == "SetTestAssetPackageTask"
       
   398         params = step.findall("./parameters/parameter")
       
   399         #assert params[0].get("repeat") == "1"
       
   400         assert params[0].get("value") == "ATS3Drop\\TestAssets\\TestAsset.zip"
       
   401         #assert params[2].get("testcase-ids") == "100"
       
   402 
       
   403     def test_post_actions(self):
       
   404         "Post actions are inserted into XML."
       
   405         xml = self.generate_xml()        
       
   406         post_actions = xml.findall(".//action")
       
   407         self.check_send_email_action(post_actions[0])
       
   408 #        self.check_ats_report_action(post_actions[1])
       
   409 #        self.check_aste_report_action(post_actions[2])
       
   410         self.check_diamonds_action(post_actions[1])
       
   411 
       
   412     def check_send_email_action(self, action):
       
   413         assert action.findtext("./type") == "EmailAction"
       
   414         params = action.findall("./parameters/parameter")
       
   415         assert params[0].get("name") == "subject"
       
   416         #assert params[0].get("value") == "email subject"
       
   417         #assert params[1].get("name") == "type"
       
   418         #assert params[1].get("value") == "ATS3_REPORT"
       
   419         #assert params[2].get("name") == "send-files"
       
   420         #assert params[2].get("value") == "true"
       
   421         assert params[1].get("name") == "to"
       
   422         assert params[1].get("value") == self.report_email
       
   423 
       
   424     def check_ats_report_action(self, action):
       
   425         assert action.findtext("./type") == "FileStoreAction"
       
   426         params = action.findall("./parameters/parameter")
       
   427         assert params[0].get("name") == "to-folder"
       
   428         assert params[0].get("value") == self.file_store
       
   429         assert params[1].get("name") == "report-type"
       
   430         assert params[1].get("value") == "ATS_REPORT"
       
   431         assert params[2].get("name") == "date-format"
       
   432         assert params[2].get("value") == "yyyyMMdd"
       
   433         assert params[3].get("name") == "time-format"
       
   434         assert params[3].get("value") == "HHmmss"
       
   435 
       
   436     def check_aste_report_action(self, action):
       
   437         assert action.findtext("./type") == "FileStoreAction"
       
   438         params = action.findall("./parameters/parameter")
       
   439         assert params[0].get("name") == "to-folder"
       
   440         assert params[0].get("value") == self.file_store
       
   441         assert params[1].get("name") == "report-type"
       
   442         assert params[1].get("value") == "ASTE_REPORT"
       
   443         assert params[2].get("name") == "run-log"
       
   444         assert params[2].get("value") == "true"
       
   445         assert params[3].get("name") == "date-format"
       
   446         assert params[3].get("value") == "yyyyMMdd"
       
   447         assert params[4].get("name") == "time-format"
       
   448         assert params[4].get("value") == "HHmmss"
       
   449         
       
   450     def check_diamonds_action(self, action):
       
   451         assert action.findtext("./type") == "DiamondsAction"
       
   452         assert not action.findall("./parameters/parameter")
       
   453     
       
   454     def test_files(self):
       
   455         xml = self.generate_xml()
       
   456 #        files = iter(xml.findall("./files/file"))
       
   457 #        assert files.next().text == r"ATS3Drop" + os.sep + "images" + os.sep + "file1.fpsx"
       
   458 #        assert files.next().text == r"ATS3Drop" + os.sep + "images" + os.sep + "file2.fpsx"
       
   459 #        assert files.next().text == r"ATS3Drop" + os.sep + "TestAssets" + os.sep + "TestAsset.zip"
       
   460 #        self.assertRaises(StopIteration, files.next)
       
   461         
       
   462     def test_generate_testasset_zip(self):
       
   463         self.generate_xml()
       
   464         if re.search(r"[.]zip", self.test_plan["testasset_location"]):
       
   465             pass
       
   466         else:
       
   467             strbuffer = StringIO()
       
   468             assert strbuffer == self.gen.generate_testasset_zip(self.test_plan, strbuffer)
       
   469             zfile = zipfile.ZipFile(strbuffer, "r")
       
   470             try:
       
   471                 contents = sorted(path(p).normpath() for p in zfile.namelist())
       
   472                 expected = sorted(path(p).normpath()
       
   473                                for p in [(r"Localisation" + os.sep + "S60" + os.sep + "localisation.txt"),
       
   474                                          (r"TestCases" + os.sep + "TC_100_Test0" + os.sep + "file1.sis"),
       
   475                                          (r"TestCases" + os.sep + "TC_100_Test0" + os.sep + "file2.tcf"),
       
   476                                          (r"Tools" + os.sep + "TestCaseCreator" + os.sep + "test_creator.ini"),
       
   477                                          (r"testdrop.xml")])
       
   478                 diff = difflib.context_diff(expected, contents)
       
   479                 assert contents == expected, "\n".join(diff)
       
   480             finally:
       
   481                 zfile.close()
       
   482         
       
   483     def test_generate_drop(self):
       
   484         "Manifest for ATS3Drop directory structure is generated."
       
   485         xml = self.generate_xml()
       
   486         strbuffer = StringIO()
       
   487 
       
   488         self.gen.generate_drop(self.test_plan, xml, strbuffer)
       
   489         zfile = zipfile.ZipFile(strbuffer, "r")
       
   490         try:
       
   491             contents = sorted(path(p).normpath() for p in zfile.namelist())
       
   492             expected = sorted(path(p).normpath()
       
   493                            for p in [r"ATS3Drop" + os.sep + "images" + os.sep + "file1.fpsx",
       
   494                                      r"ATS3Drop" + os.sep + "images" + os.sep + "file2.fpsx",
       
   495                                      r"ATS3Drop" + os.sep + "TestAssets" + os.sep + "TestAsset.zip",
       
   496                                      r"test.xml"])
       
   497             diff = difflib.context_diff(expected, contents)
       
   498             assert contents == expected, "\n".join(diff)
       
   499         finally:
       
   500             zfile.close()