Am 20.10.2012 15:47, schrieb Paolo Bonzini: > Il 19/10/2012 18:19, Kevin Wolf ha scritto: >> Am 18.10.2012 16:49, schrieb Paolo Bonzini: >>> Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> >>> --- >>> v2->v3: new testcases test_cancel_after_ready and >>> test_medium_not_found, removed obsolete workaround >>> for os.remove failure. Fixed copyright header. >>> >>> tests/qemu-iotests/041 | 364 >>> +++++++++++++++++++++++++++++++++++++++++++++ >>> tests/qemu-iotests/041.out | 5 + >>> tests/qemu-iotests/group | 1 + >>> 3 file modificati, 370 inserzioni(+) >>> create mode 100755 tests/qemu-iotests/041 >>> create mode 100644 tests/qemu-iotests/041.out >>> >>> diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 >>> new file mode 100755 >>> index 0000000..ce99b00 >>> --- /dev/null >>> +++ b/tests/qemu-iotests/041 >>> @@ -0,0 +1,364 @@ >>> +#!/usr/bin/env python >>> +# >>> +# Tests for image mirroring. >>> +# >>> +# Copyright (C) 2012 Red Hat, Inc. >>> +# >>> +# This program is free software; you can redistribute it and/or modify >>> +# it under the terms of the GNU General Public License as published by >>> +# the Free Software Foundation; either version 2 of the License, or >>> +# (at your option) any later version. >>> +# >>> +# This program is distributed in the hope that it will be useful, >>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of >>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>> +# GNU General Public License for more details. >>> +# >>> +# You should have received a copy of the GNU General Public License >>> +# along with this program. If not, see <http://www.gnu.org/licenses/>. >>> +# >>> + >>> +import time >>> +import os >>> +import iotests >>> +from iotests import qemu_img, qemu_io >>> +import struct >>> + >>> +backing_img = os.path.join(iotests.test_dir, 'backing.img') >>> +target_backing_img = os.path.join(iotests.test_dir, 'target-backing.img') >>> +test_img = os.path.join(iotests.test_dir, 'test.img') >>> +target_img = os.path.join(iotests.test_dir, 'target.img') >>> + >>> +class ImageMirroringTestCase(iotests.QMPTestCase): >>> + '''Abstract base class for image mirroring test cases''' >>> + >>> + def assert_no_active_mirrors(self): >>> + result = self.vm.qmp('query-block-jobs') >>> + self.assert_qmp(result, 'return', []) >>> + >>> + def cancel_and_wait(self, drive='drive0', wait_ready=True): >>> + '''Cancel a block job and wait for it to finish''' >>> + if wait_ready: >>> + ready = False >>> + while not ready: >>> + for event in self.vm.get_qmp_events(wait=True): >>> + if event['event'] == 'BLOCK_JOB_READY': >>> + self.assert_qmp(event, 'data/type', 'mirror') >>> + self.assert_qmp(event, 'data/device', drive) >>> + ready = True >>> + >>> + result = self.vm.qmp('block-job-cancel', device=drive, >>> + force=not wait_ready) >>> + self.assert_qmp(result, 'return', {}) >>> + >>> + cancelled = False >>> + while not cancelled: >>> + for event in self.vm.get_qmp_events(wait=True): >>> + if event['event'] == 'BLOCK_JOB_COMPLETED' or \ >>> + event['event'] == 'BLOCK_JOB_CANCELLED': >>> + self.assert_qmp(event, 'data/type', 'mirror') >>> + self.assert_qmp(event, 'data/device', drive) >>> + if wait_ready: >>> + self.assertEquals(event['event'], >>> 'BLOCK_JOB_COMPLETED') >>> + self.assert_qmp(event, 'data/offset', >>> self.image_len) >>> + self.assert_qmp(event, 'data/len', self.image_len) >>> + cancelled = True >>> + >>> + self.assert_no_active_mirrors() >>> + >>> + def complete_and_wait(self, drive='drive0', wait_ready=True): >>> + '''Complete a block job and wait for it to finish''' >>> + if wait_ready: >>> + ready = False >>> + while not ready: >>> + for event in self.vm.get_qmp_events(wait=True): >>> + if event['event'] == 'BLOCK_JOB_READY': >>> + self.assert_qmp(event, 'data/type', 'mirror') >>> + self.assert_qmp(event, 'data/device', drive) >>> + ready = True >>> + >>> + result = self.vm.qmp('block-job-complete', device=drive) >>> + self.assert_qmp(result, 'return', {}) >>> + >>> + completed = False >>> + while not completed: >>> + for event in self.vm.get_qmp_events(wait=True): >>> + if event['event'] == 'BLOCK_JOB_COMPLETED': >>> + self.assert_qmp(event, 'data/type', 'mirror') >>> + self.assert_qmp(event, 'data/device', drive) >>> + self.assert_qmp_absent(event, 'data/error') >>> + self.assert_qmp(event, 'data/offset', self.image_len) >>> + self.assert_qmp(event, 'data/len', self.image_len) >>> + completed = True >>> + >>> + self.assert_no_active_mirrors() >>> + >>> + def create_image(self, name, size): >>> + file = open(name, 'w') >>> + i = 0 >>> + while i < size: >>> + sector = struct.pack('>l504xl', i / 512, i / 512) >>> + file.write(sector) >>> + i = i + 512 >>> + file.close() >>> + >>> + def compare_images(self, img1, img2): >>> + try: >>> + qemu_img('convert', '-f', iotests.imgfmt, '-O', 'raw', img1, >>> img1 + '.raw') >>> + qemu_img('convert', '-f', iotests.imgfmt, '-O', 'raw', img2, >>> img2 + '.raw') >>> + file1 = open(img1 + '.raw', 'r') >>> + file2 = open(img2 + '.raw', 'r') >>> + return file1.read() == file2.read() >>> + finally: >>> + if file1 is not None: >>> + file1.close() >>> + if file2 is not None: >>> + file2.close() >>> + try: >>> + os.remove(img1 + '.raw') >>> + except OSError: >>> + pass >>> + try: >>> + os.remove(img2 + '.raw') >>> + except OSError: >>> + pass >>> + >>> +class TestSingleDrive(ImageMirroringTestCase): >>> + image_len = 1 * 1024 * 1024 # MB >>> + >>> + def setUp(self): >>> + self.create_image(backing_img, TestSingleDrive.image_len) >>> + qemu_img('create', '-f', iotests.imgfmt, '-o', 'backing_file=%s' % >>> backing_img, test_img) >>> + self.vm = iotests.VM().add_drive(test_img) >>> + self.vm.launch() >>> + >>> + def tearDown(self): >>> + self.vm.shutdown() >>> + os.remove(test_img) >>> + os.remove(backing_img) >>> + try: >>> + os.remove(target_img) >>> + except OSError: >>> + pass >>> + >>> + def test_complete(self): >>> + self.assert_no_active_mirrors() >>> + >>> + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', >>> + target=target_img) >>> + self.assert_qmp(result, 'return', {}) >>> + >>> + self.complete_and_wait() >>> + result = self.vm.qmp('query-block') >>> + self.assert_qmp(result, 'return[0]/inserted/file', target_img) >>> + self.vm.shutdown() >>> + self.assertTrue(self.compare_images(test_img, target_img), >>> + 'target image does not match source after >>> mirroring') >>> + >>> + def test_cancel(self): >>> + self.assert_no_active_mirrors() >>> + >>> + result = self.vm.qmp('drive-mirror', device='drive0', sync='full', >>> + target=target_img) >>> + self.assert_qmp(result, 'return', {}) >>> + >>> + self.cancel_and_wait(wait_ready=False) >>> + result = self.vm.qmp('query-block') >>> + self.assert_qmp(result, 'return[0]/inserted/file', test_img) >>> + self.vm.shutdown() >>> + self.assertTrue(self.compare_images(test_img, target_img), >>> + 'target image does not match source after >>> mirroring') >> >> Hm, shouldn't the image comparison actually fail when cancelling before >> the job was ready? > > It's racy, it is probably completing anyway. Should I send a v4 with a > regenerated .out and the assertion removed?
Yes, please. Can we additionaly increase our chances to win the race, for example by specifying a very low speed? Kevin