* [PATCH 0/2] iotests: complicate run_job with this one weird trick?
@ 2020-02-26 0:44 John Snow
2020-02-26 0:44 ` [PATCH 1/2] iotests: add JobRunner class John Snow
2020-02-26 0:44 ` [PATCH 2/2] iotests: modify test 040 to use JobRunner John Snow
0 siblings, 2 replies; 9+ messages in thread
From: John Snow @ 2020-02-26 0:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, John Snow, qemu-block, Max Reitz
Requires: 20200225005641.5478-1-jsnow@redhat.com
I'm kidding, but do treat this as an RFC. I doodled a little code
change and wasn't sure if it was appropriate because it's not really a
simplification.
The basic idea is to make a generic job runtime manager and allow
callers to subclass the manager. Then, instead of adding callback
arguments to the function all the time, we have à la carte customization
of the loop.
To showcase this a little bit, I removed the pre_finalization argument
and made existing callers use a custom JobRunner; and then converted
test 040 to use this style of job runner.
Letmeknowwhatchathink.
John Snow (2):
iotests: add JobRunner class
iotests: modify test 040 to use JobRunner
tests/qemu-iotests/040 | 51 +++++-----
tests/qemu-iotests/255 | 9 +-
tests/qemu-iotests/257 | 12 ++-
tests/qemu-iotests/287 | 19 +++-
tests/qemu-iotests/iotests.py | 176 ++++++++++++++++++++++++----------
5 files changed, 183 insertions(+), 84 deletions(-)
--
2.21.1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/2] iotests: add JobRunner class
2020-02-26 0:44 [PATCH 0/2] iotests: complicate run_job with this one weird trick? John Snow
@ 2020-02-26 0:44 ` John Snow
2020-02-26 11:18 ` Max Reitz
2020-02-26 0:44 ` [PATCH 2/2] iotests: modify test 040 to use JobRunner John Snow
1 sibling, 1 reply; 9+ messages in thread
From: John Snow @ 2020-02-26 0:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, John Snow, qemu-block, Max Reitz
The idea is that instead of increasing the arguments to job_run all the
time, create a more general-purpose job runner that can be subclassed to
do interesting things with.
Signed-off-by: John Snow <jsnow@redhat.com>
---
tests/qemu-iotests/255 | 9 +-
tests/qemu-iotests/257 | 12 ++-
tests/qemu-iotests/287 | 19 +++-
tests/qemu-iotests/iotests.py | 176 ++++++++++++++++++++++++----------
4 files changed, 158 insertions(+), 58 deletions(-)
diff --git a/tests/qemu-iotests/255 b/tests/qemu-iotests/255
index 4a4818bafb..513e9ebb58 100755
--- a/tests/qemu-iotests/255
+++ b/tests/qemu-iotests/255
@@ -71,8 +71,13 @@ with iotests.FilePath('t.qcow2') as disk_path, \
result = vm.qmp_log('block-commit', job_id='job0', auto_finalize=False,
device='overlay', top_node='mid')
- vm.run_job('job0', auto_finalize=False, pre_finalize=start_requests,
- auto_dismiss=True)
+ class TestJobRunner(iotests.JobRunner):
+ def on_pending(self, event):
+ start_requests()
+ super().on_pending(event)
+
+ runner = TestJobRunner(vm, 'job0', auto_finalize=False, auto_dismiss=True)
+ runner.run()
vm.shutdown()
diff --git a/tests/qemu-iotests/257 b/tests/qemu-iotests/257
index 2a81f9e30c..e73b0c20b3 100755
--- a/tests/qemu-iotests/257
+++ b/tests/qemu-iotests/257
@@ -265,9 +265,15 @@ def test_bitmap_sync(bsync_mode, msync_mode='bitmap', failure=None):
ebitmap.clear()
ebitmap.dirty_group(2)
- vm.run_job(job, auto_dismiss=True, auto_finalize=False,
- pre_finalize=_callback,
- cancel=(failure == 'simulated'))
+ class TestJobRunner(iotests.JobRunner):
+ def on_pending(self, event):
+ _callback()
+ super().on_pending(event)
+
+ runner = TestJobRunner(vm, job, cancel=(failure == 'simulated'),
+ auto_finalize=False, auto_dismiss=True)
+ runner.run()
+
bitmaps = vm.query_bitmaps()
log({'bitmaps': bitmaps}, indent=2)
log('')
diff --git a/tests/qemu-iotests/287 b/tests/qemu-iotests/287
index 0ab58dc011..f06e6ff084 100755
--- a/tests/qemu-iotests/287
+++ b/tests/qemu-iotests/287
@@ -165,13 +165,22 @@ def test_bitmap_populate(config):
if not config.disabled:
ebitmap.dirty_group(2)
+
+ class TestJobRunner(iotests.JobRunner):
+ def on_pending(self, event):
+ if config.mid_writes:
+ perform_writes(drive0, 2)
+ if not config.disabled:
+ ebitmap.dirty_group(2)
+ super().on_pending(event)
+
job = populate(drive0, 'target', 'bitpop0')
assert job['return'] == {'return': {}}
- vm.run_job(job['id'],
- auto_dismiss=job['auto-dismiss'],
- auto_finalize=job['auto-finalize'],
- pre_finalize=pre_finalize,
- cancel=config.cancel)
+ job_runner = TestJobRunner(vm, job['id'],
+ auto_dismiss=job['auto-dismiss'],
+ auto_finalize=job['auto-finalize'],
+ cancel=config.cancel)
+ job_runner.run()
log('')
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 3390fab021..37a8b4d649 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -460,6 +460,130 @@ def remote_filename(path):
else:
raise Exception("Protocol %s not supported" % (imgproto))
+
+class JobRunner:
+ def __init__(self, vm, job,
+ use_log=True,
+ cancel=False,
+ auto_finalize=True,
+ auto_dismiss=False):
+ self._vm = vm
+ self._id = job
+ self.logging = use_log
+ self.cancel = cancel
+
+ self._auto_finalize = auto_finalize
+ self._auto_dismiss = auto_dismiss
+ self._exited = False
+ self._error = None
+
+ match_device = {'data': {'device': job}}
+ match_id = {'data': {'id': job}}
+ self._events = {
+ 'BLOCK_JOB_COMPLETED': match_device,
+ 'BLOCK_JOB_CANCELLED': match_device,
+ 'BLOCK_JOB_ERROR': match_device,
+ 'BLOCK_JOB_READY': match_device,
+ 'BLOCK_JOB_PENDING': match_id,
+ 'JOB_STATUS_CHANGE': match_id
+ }
+
+ self._dispatch = {
+ 'created': self.on_create,
+ 'running': self.on_run,
+ 'paused': self.on_pause,
+ 'ready': self.on_ready,
+ 'standby': self.on_standby,
+ 'waiting': self.on_waiting,
+ 'pending': self.on_pending,
+ 'aborting': self.on_abort,
+ 'concluded': self.on_conclude,
+ 'null': self.on_null,
+ }
+
+ # Job events -- state changes.
+
+ def on_create(self, event):
+ pass
+
+ def on_run(self, event):
+ pass
+
+ def on_pause(self, event):
+ pass
+
+ def on_ready(self, event):
+ if self.logging:
+ self._vm.qmp_log('job-complete', id=self._id)
+ else:
+ self._vm.qmp('job-complete', id=self._id)
+
+ def on_standby(self, event):
+ pass
+
+ def on_waiting(self, event):
+ pass
+
+ def on_pending(self, event):
+ if self._auto_finalize:
+ return
+
+ if self.cancel:
+ if self.logging:
+ self._vm.qmp_log('job-cancel', id=self._id)
+ else:
+ self._vm.qmp('job-cancel', id=self._id)
+ else:
+ if self.logging:
+ self._vm.qmp_log('job-finalize', id=self._id)
+ else:
+ self._vm.qmp('job-finalize', id=self._id)
+
+ def on_abort(self, event):
+ result = self._vm.qmp('query-jobs')
+ for j in result['return']:
+ if j['id'] == self._id:
+ self.error = j['error']
+ if self.logging:
+ log('Job failed: %s' % (j['error']))
+
+ def on_conclude(self, event):
+ if self._auto_dismiss:
+ return
+
+ if self.logging:
+ self._vm.qmp_log('job-dismiss', id=self._id)
+ else:
+ self._vm.qmp('job-dismiss', id=self._id)
+
+ def on_null(self, event):
+ self._exited = True
+
+ # Macro events -- QAPI events
+
+ def on_change(self, event):
+ status = event['data']['status']
+ assert status in self._dispatch
+ self._dispatch[status](event)
+
+ def on_block_job_event(self, event):
+ if self.logging:
+ log(event)
+
+ def _event(self, event):
+ assert event['event'] in self._events.keys()
+ if event['event'] == 'JOB_STATUS_CHANGE':
+ self.on_change(event)
+ else:
+ self.on_block_job_event(event)
+
+ def run(self, wait=60.0):
+ while not self._exited:
+ raw_event = self._vm.events_wait(self._events, timeout=wait)
+ self._event(filter_qmp_event(raw_event))
+ return self._error
+
+
class VM(qtest.QEMUQtestMachine):
'''A QEMU VM'''
@@ -585,7 +709,7 @@ def qmp_log(self, cmd, filters=[], indent=None, **kwargs):
# Returns None on success, and an error string on failure
def run_job(self, job, auto_finalize=True, auto_dismiss=False,
- pre_finalize=None, cancel=False, use_log=True, wait=60.0):
+ cancel=False, use_log=True, wait=60.0):
"""
run_job moves a job from creation through to dismissal.
@@ -594,59 +718,15 @@ def run_job(self, job, auto_finalize=True, auto_dismiss=False,
auto_finalize. Defaults to True.
:param auto_dismiss: Bool. True if the job was launched with
auto_dismiss=True. Defaults to False.
- :param pre_finalize: Callback. A callable that takes no arguments to be
- invoked prior to issuing job-finalize, if any.
:param cancel: Bool. When true, cancels the job after the pre_finalize
callback.
:param use_log: Bool. When false, does not log QMP messages.
:param wait: Float. Timeout value specifying how long to wait for any
event, in seconds. Defaults to 60.0.
"""
- match_device = {'data': {'device': job}}
- match_id = {'data': {'id': job}}
- events = {
- 'BLOCK_JOB_COMPLETED': match_device,
- 'BLOCK_JOB_CANCELLED': match_device,
- 'BLOCK_JOB_ERROR': match_device,
- 'BLOCK_JOB_READY': match_device,
- 'BLOCK_JOB_PENDING': match_id,
- 'JOB_STATUS_CHANGE': match_id,
- }
- error = None
- while True:
- ev = filter_qmp_event(self.events_wait(events, timeout=wait))
- if ev['event'] != 'JOB_STATUS_CHANGE':
- if use_log:
- log(ev)
- continue
- status = ev['data']['status']
- if status == 'aborting':
- result = self.qmp('query-jobs')
- for j in result['return']:
- if j['id'] == job:
- error = j['error']
- if use_log:
- log('Job failed: %s' % (j['error']))
- elif status == 'ready':
- self.qmp_log('job-complete', id=job)
- elif status == 'pending' and not auto_finalize:
- if pre_finalize:
- pre_finalize()
- if cancel and use_log:
- self.qmp_log('job-cancel', id=job)
- elif cancel:
- self.qmp('job-cancel', id=job)
- elif use_log:
- self.qmp_log('job-finalize', id=job)
- else:
- self.qmp('job-finalize', id=job)
- elif status == 'concluded' and not auto_dismiss:
- if use_log:
- self.qmp_log('job-dismiss', id=job)
- else:
- self.qmp('job-dismiss', id=job)
- elif status == 'null':
- return error
+ job_runner = JobRunner(self, job, use_log, cancel,
+ auto_finalize, auto_dismiss)
+ return job_runner.run(wait=wait)
# Returns None on success, and an error string on failure
def blockdev_create(self, options, job_id='job0', filters=None):
--
2.21.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/2] iotests: modify test 040 to use JobRunner
2020-02-26 0:44 [PATCH 0/2] iotests: complicate run_job with this one weird trick? John Snow
2020-02-26 0:44 ` [PATCH 1/2] iotests: add JobRunner class John Snow
@ 2020-02-26 0:44 ` John Snow
2020-02-26 11:31 ` Max Reitz
1 sibling, 1 reply; 9+ messages in thread
From: John Snow @ 2020-02-26 0:44 UTC (permalink / raw)
To: qemu-devel; +Cc: Kevin Wolf, John Snow, qemu-block, Max Reitz
Instead of having somewhat reproduced it for itself.
Signed-off-by: John Snow <jsnow@redhat.com>
---
tests/qemu-iotests/040 | 51 +++++++++++++++++++++---------------------
1 file changed, 25 insertions(+), 26 deletions(-)
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
index 90b59081ff..579dafc797 100755
--- a/tests/qemu-iotests/040
+++ b/tests/qemu-iotests/040
@@ -483,34 +483,33 @@ class TestErrorHandling(iotests.QMPTestCase):
file=('top-dbg' if top_debug else 'top-file'),
backing='mid-fmt')
+
+ class TestJobRunner(iotests.JobRunner):
+ expected_events = ('BLOCK_JOB_COMPLETED',
+ 'BLOCK_JOB_ERROR',
+ 'BLOCK_JOB_READY')
+
+ def __init__(self, *args, test, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.log = []
+ self.test = test
+
+ def on_pause(self, event):
+ result = self._vm.qmp('block-job-resume', device=self._id)
+ self.test.assert_qmp(result, 'return', {})
+ super().on_pause(event)
+
+ def on_block_job_event(self, event):
+ if event['event'] not in self.expected_events:
+ self.test.fail("Unexpected event: %s" % event)
+ super().on_block_job_event(event)
+ self.log.append(iotests.filter_qmp_event(event))
+
def run_job(self, expected_events, error_pauses_job=False):
- match_device = {'data': {'device': 'job0'}}
- events = {
- 'BLOCK_JOB_COMPLETED': match_device,
- 'BLOCK_JOB_CANCELLED': match_device,
- 'BLOCK_JOB_ERROR': match_device,
- 'BLOCK_JOB_READY': match_device,
- }
-
- completed = False
- log = []
- while not completed:
- ev = self.vm.events_wait(events, timeout=5.0)
- if ev['event'] == 'BLOCK_JOB_COMPLETED':
- completed = True
- elif ev['event'] == 'BLOCK_JOB_ERROR':
- if error_pauses_job:
- result = self.vm.qmp('block-job-resume', device='job0')
- self.assert_qmp(result, 'return', {})
- elif ev['event'] == 'BLOCK_JOB_READY':
- result = self.vm.qmp('block-job-complete', device='job0')
- self.assert_qmp(result, 'return', {})
- else:
- self.fail("Unexpected event: %s" % ev)
- log.append(iotests.filter_qmp_event(ev))
-
+ job = self.TestJobRunner(self.vm, 'job0', use_log=False, test=self)
+ job.run()
self.maxDiff = None
- self.assertEqual(expected_events, log)
+ self.assertEqual(expected_events, job.log)
def event_error(self, op, action):
return {
--
2.21.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] iotests: add JobRunner class
2020-02-26 0:44 ` [PATCH 1/2] iotests: add JobRunner class John Snow
@ 2020-02-26 11:18 ` Max Reitz
2020-02-26 17:58 ` John Snow
0 siblings, 1 reply; 9+ messages in thread
From: Max Reitz @ 2020-02-26 11:18 UTC (permalink / raw)
To: John Snow, qemu-devel; +Cc: Kevin Wolf, qemu-block
[-- Attachment #1.1: Type: text/plain, Size: 2900 bytes --]
On 26.02.20 01:44, John Snow wrote:
> The idea is that instead of increasing the arguments to job_run all the
> time, create a more general-purpose job runner that can be subclassed to
> do interesting things with.
>
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
> tests/qemu-iotests/255 | 9 +-
> tests/qemu-iotests/257 | 12 ++-
> tests/qemu-iotests/287 | 19 +++-
> tests/qemu-iotests/iotests.py | 176 ++++++++++++++++++++++++----------
> 4 files changed, 158 insertions(+), 58 deletions(-)
I like it!
[...]
> diff --git a/tests/qemu-iotests/287 b/tests/qemu-iotests/287
> index 0ab58dc011..f06e6ff084 100755
> --- a/tests/qemu-iotests/287
> +++ b/tests/qemu-iotests/287
> @@ -165,13 +165,22 @@ def test_bitmap_populate(config):
> if not config.disabled:
> ebitmap.dirty_group(2)
>
> +
> + class TestJobRunner(iotests.JobRunner):
> + def on_pending(self, event):
> + if config.mid_writes:
> + perform_writes(drive0, 2)
> + if not config.disabled:
> + ebitmap.dirty_group(2)
I actually prefer inlining the pre_finalize() functions (over calling
the existing one), but then we can also remove the original function. :)
> + super().on_pending(event)
> +
> job = populate(drive0, 'target', 'bitpop0')
> assert job['return'] == {'return': {}}
> - vm.run_job(job['id'],
> - auto_dismiss=job['auto-dismiss'],
> - auto_finalize=job['auto-finalize'],
> - pre_finalize=pre_finalize,
> - cancel=config.cancel)
> + job_runner = TestJobRunner(vm, job['id'],
> + auto_dismiss=job['auto-dismiss'],
> + auto_finalize=job['auto-finalize'],
> + cancel=config.cancel)
> + job_runner.run()
> log('')
>
>
> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
> index 3390fab021..37a8b4d649 100644
> --- a/tests/qemu-iotests/iotests.py
> +++ b/tests/qemu-iotests/iotests.py
> @@ -460,6 +460,130 @@ def remote_filename(path):
> else:
> raise Exception("Protocol %s not supported" % (imgproto))
>
> +
> +class JobRunner:
[...]
> + def on_ready(self, event):
> + if self.logging:
> + self._vm.qmp_log('job-complete', id=self._id)
> + else:
> + self._vm.qmp('job-complete', id=self._id)
I suppose this is a bug fix. (The old version always called qmp_log.)
But what about adding a do_qmp method to JobRunner that does the
“if self.logging { self._vm.qmp_log() } else { self._vm.qmp }” part so
we don’t have to inline that everywhere?
Max
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] iotests: modify test 040 to use JobRunner
2020-02-26 0:44 ` [PATCH 2/2] iotests: modify test 040 to use JobRunner John Snow
@ 2020-02-26 11:31 ` Max Reitz
2020-02-26 18:04 ` John Snow
0 siblings, 1 reply; 9+ messages in thread
From: Max Reitz @ 2020-02-26 11:31 UTC (permalink / raw)
To: John Snow, qemu-devel; +Cc: Kevin Wolf, qemu-block
[-- Attachment #1.1: Type: text/plain, Size: 3280 bytes --]
On 26.02.20 01:44, John Snow wrote:
> Instead of having somewhat reproduced it for itself.
>
> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
> tests/qemu-iotests/040 | 51 +++++++++++++++++++++---------------------
> 1 file changed, 25 insertions(+), 26 deletions(-)
>
> diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
> index 90b59081ff..579dafc797 100755
> --- a/tests/qemu-iotests/040
> +++ b/tests/qemu-iotests/040
> @@ -483,34 +483,33 @@ class TestErrorHandling(iotests.QMPTestCase):
> file=('top-dbg' if top_debug else 'top-file'),
> backing='mid-fmt')
>
> +
> + class TestJobRunner(iotests.JobRunner):
> + expected_events = ('BLOCK_JOB_COMPLETED',
> + 'BLOCK_JOB_ERROR',
> + 'BLOCK_JOB_READY')
> +
> + def __init__(self, *args, test, **kwargs):
> + super().__init__(*args, **kwargs)
> + self.log = []
> + self.test = test
> +
> + def on_pause(self, event):
> + result = self._vm.qmp('block-job-resume', device=self._id)
> + self.test.assert_qmp(result, 'return', {})
> + super().on_pause(event)
Not that it functionally matters, but I suppose I’d call
super().on_pause() before resuming (because the job isn’t exactly paused
afterwards).
> +
> + def on_block_job_event(self, event):
> + if event['event'] not in self.expected_events:
> + self.test.fail("Unexpected event: %s" % event)
> + super().on_block_job_event(event)
> + self.log.append(iotests.filter_qmp_event(event))
Hasn’t the event been through filter_qmp_event() already?
Max
> +
> def run_job(self, expected_events, error_pauses_job=False):
> - match_device = {'data': {'device': 'job0'}}
> - events = {
> - 'BLOCK_JOB_COMPLETED': match_device,
> - 'BLOCK_JOB_CANCELLED': match_device,
> - 'BLOCK_JOB_ERROR': match_device,
> - 'BLOCK_JOB_READY': match_device,
> - }
> -
> - completed = False
> - log = []
> - while not completed:
> - ev = self.vm.events_wait(events, timeout=5.0)
> - if ev['event'] == 'BLOCK_JOB_COMPLETED':
> - completed = True
> - elif ev['event'] == 'BLOCK_JOB_ERROR':
> - if error_pauses_job:
> - result = self.vm.qmp('block-job-resume', device='job0')
> - self.assert_qmp(result, 'return', {})
> - elif ev['event'] == 'BLOCK_JOB_READY':
> - result = self.vm.qmp('block-job-complete', device='job0')
> - self.assert_qmp(result, 'return', {})
> - else:
> - self.fail("Unexpected event: %s" % ev)
> - log.append(iotests.filter_qmp_event(ev))
> -
> + job = self.TestJobRunner(self.vm, 'job0', use_log=False, test=self)
> + job.run()
> self.maxDiff = None
> - self.assertEqual(expected_events, log)
> + self.assertEqual(expected_events, job.log)
>
> def event_error(self, op, action):
> return {
>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] iotests: add JobRunner class
2020-02-26 11:18 ` Max Reitz
@ 2020-02-26 17:58 ` John Snow
2020-02-27 11:44 ` Max Reitz
0 siblings, 1 reply; 9+ messages in thread
From: John Snow @ 2020-02-26 17:58 UTC (permalink / raw)
To: Max Reitz, qemu-devel; +Cc: Kevin Wolf, qemu-block
On 2/26/20 6:18 AM, Max Reitz wrote:
> On 26.02.20 01:44, John Snow wrote:
>> The idea is that instead of increasing the arguments to job_run all the
>> time, create a more general-purpose job runner that can be subclassed to
>> do interesting things with.
>>
>> Signed-off-by: John Snow <jsnow@redhat.com>
>> ---
>> tests/qemu-iotests/255 | 9 +-
>> tests/qemu-iotests/257 | 12 ++-
>> tests/qemu-iotests/287 | 19 +++-
>> tests/qemu-iotests/iotests.py | 176 ++++++++++++++++++++++++----------
>> 4 files changed, 158 insertions(+), 58 deletions(-)
>
> I like it!
>
High praise!
> [...]
>
>> diff --git a/tests/qemu-iotests/287 b/tests/qemu-iotests/287
>> index 0ab58dc011..f06e6ff084 100755
>> --- a/tests/qemu-iotests/287
>> +++ b/tests/qemu-iotests/287
>> @@ -165,13 +165,22 @@ def test_bitmap_populate(config):
>> if not config.disabled:
>> ebitmap.dirty_group(2)
>>
>> +
>> + class TestJobRunner(iotests.JobRunner):
>> + def on_pending(self, event):
>> + if config.mid_writes:
>> + perform_writes(drive0, 2)
>> + if not config.disabled:
>> + ebitmap.dirty_group(2)
>
> I actually prefer inlining the pre_finalize() functions (over calling
> the existing one), but then we can also remove the original function. :)
>
Not sure I understand you correctly. You're saying you prefer this
strategy where I inline the logic vs others where I call out to a function?
If so, I agree if only for purity -- the function looks and acts like a
callback instead of a callback-that-calls-another-callback.
>> + super().on_pending(event)
>> +
>> job = populate(drive0, 'target', 'bitpop0')
>> assert job['return'] == {'return': {}}
>> - vm.run_job(job['id'],
>> - auto_dismiss=job['auto-dismiss'],
>> - auto_finalize=job['auto-finalize'],
>> - pre_finalize=pre_finalize,
>> - cancel=config.cancel)
>> + job_runner = TestJobRunner(vm, job['id'],
>> + auto_dismiss=job['auto-dismiss'],
>> + auto_finalize=job['auto-finalize'],
>> + cancel=config.cancel)
>> + job_runner.run()
>> log('')
>>
>>
>> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
>> index 3390fab021..37a8b4d649 100644
>> --- a/tests/qemu-iotests/iotests.py
>> +++ b/tests/qemu-iotests/iotests.py
>> @@ -460,6 +460,130 @@ def remote_filename(path):
>> else:
>> raise Exception("Protocol %s not supported" % (imgproto))
>>
>> +
>> +class JobRunner:
>
> [...]
>
>> + def on_ready(self, event):
>> + if self.logging:
>> + self._vm.qmp_log('job-complete', id=self._id)
>> + else:
>> + self._vm.qmp('job-complete', id=self._id)
>
> I suppose this is a bug fix. (The old version always called qmp_log.)
>
Technically yes. It was needed for 040.
> But what about adding a do_qmp method to JobRunner that does the
> “if self.logging { self._vm.qmp_log() } else { self._vm.qmp }” part so
> we don’t have to inline that everywhere?
>
> Max
>
I'll just clean up the logging series I had to do it at a more
fundamental level.
Just testing the temperature of the water.
--js
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/2] iotests: modify test 040 to use JobRunner
2020-02-26 11:31 ` Max Reitz
@ 2020-02-26 18:04 ` John Snow
0 siblings, 0 replies; 9+ messages in thread
From: John Snow @ 2020-02-26 18:04 UTC (permalink / raw)
To: Max Reitz, qemu-devel; +Cc: Kevin Wolf, qemu-block
On 2/26/20 6:31 AM, Max Reitz wrote:
> On 26.02.20 01:44, John Snow wrote:
>> Instead of having somewhat reproduced it for itself.
>>
>> Signed-off-by: John Snow <jsnow@redhat.com>
>> ---
>> tests/qemu-iotests/040 | 51 +++++++++++++++++++++---------------------
>> 1 file changed, 25 insertions(+), 26 deletions(-)
>>
>> diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
>> index 90b59081ff..579dafc797 100755
>> --- a/tests/qemu-iotests/040
>> +++ b/tests/qemu-iotests/040
>> @@ -483,34 +483,33 @@ class TestErrorHandling(iotests.QMPTestCase):
>> file=('top-dbg' if top_debug else 'top-file'),
>> backing='mid-fmt')
>>
>> +
>> + class TestJobRunner(iotests.JobRunner):
>> + expected_events = ('BLOCK_JOB_COMPLETED',
>> + 'BLOCK_JOB_ERROR',
>> + 'BLOCK_JOB_READY')
>> +
>> + def __init__(self, *args, test, **kwargs):
>> + super().__init__(*args, **kwargs)
>> + self.log = []
>> + self.test = test
>> +
>> + def on_pause(self, event):
>> + result = self._vm.qmp('block-job-resume', device=self._id)
>> + self.test.assert_qmp(result, 'return', {})
>> + super().on_pause(event)
>
> Not that it functionally matters, but I suppose I’d call
> super().on_pause() before resuming (because the job isn’t exactly paused
> afterwards).
>
Reasonable detail to consider.
It's also likely valid to just *omit* calling the base class pause event
when overriding behavior -- If we decide to send resume commands in the
future, we'll want to avoid sending conflicting/duplicate events.
In this case, the base event is just a NOP so it doesn't matter, but it
probably is good hygiene to avoid changing the state *and then* calling
the base class.
So I think this is a valid observation that should be worked into the
docstring for the JobRunner class on how best to make use of it.
>> +
>> + def on_block_job_event(self, event):
>> + if event['event'] not in self.expected_events:
>> + self.test.fail("Unexpected event: %s" % event)
>> + super().on_block_job_event(event)
>> + self.log.append(iotests.filter_qmp_event(event))
>
> Hasn’t the event been through filter_qmp_event() already?
>
Oh, yeah. When I converted 040 here I just kind of shoehorned it onto
the new API in a somewhat mechanical fashion, but you're right.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] iotests: add JobRunner class
2020-02-26 17:58 ` John Snow
@ 2020-02-27 11:44 ` Max Reitz
2020-03-03 21:32 ` John Snow
0 siblings, 1 reply; 9+ messages in thread
From: Max Reitz @ 2020-02-27 11:44 UTC (permalink / raw)
To: John Snow, qemu-devel; +Cc: Kevin Wolf, qemu-block
[-- Attachment #1.1: Type: text/plain, Size: 3799 bytes --]
On 26.02.20 18:58, John Snow wrote:
>
>
> On 2/26/20 6:18 AM, Max Reitz wrote:
>> On 26.02.20 01:44, John Snow wrote:
>>> The idea is that instead of increasing the arguments to job_run all the
>>> time, create a more general-purpose job runner that can be subclassed to
>>> do interesting things with.
>>>
>>> Signed-off-by: John Snow <jsnow@redhat.com>
>>> ---
>>> tests/qemu-iotests/255 | 9 +-
>>> tests/qemu-iotests/257 | 12 ++-
>>> tests/qemu-iotests/287 | 19 +++-
>>> tests/qemu-iotests/iotests.py | 176 ++++++++++++++++++++++++----------
>>> 4 files changed, 158 insertions(+), 58 deletions(-)
>>
>> I like it!
>>
>
> High praise!
>
>> [...]
>>
>>> diff --git a/tests/qemu-iotests/287 b/tests/qemu-iotests/287
>>> index 0ab58dc011..f06e6ff084 100755
>>> --- a/tests/qemu-iotests/287
>>> +++ b/tests/qemu-iotests/287
>>> @@ -165,13 +165,22 @@ def test_bitmap_populate(config):
>>> if not config.disabled:
>>> ebitmap.dirty_group(2)
>>>
>>> +
>>> + class TestJobRunner(iotests.JobRunner):
>>> + def on_pending(self, event):
>>> + if config.mid_writes:
>>> + perform_writes(drive0, 2)
>>> + if not config.disabled:
>>> + ebitmap.dirty_group(2)
>>
>> I actually prefer inlining the pre_finalize() functions (over calling
>> the existing one), but then we can also remove the original function. :)
>>
>
> Not sure I understand you correctly. You're saying you prefer this
> strategy where I inline the logic vs others where I call out to a function?
Yes.
> If so, I agree if only for purity -- the function looks and acts like a
> callback instead of a callback-that-calls-another-callback.
That was my thinking.
>>> + super().on_pending(event)
>>> +
>>> job = populate(drive0, 'target', 'bitpop0')
>>> assert job['return'] == {'return': {}}
>>> - vm.run_job(job['id'],
>>> - auto_dismiss=job['auto-dismiss'],
>>> - auto_finalize=job['auto-finalize'],
>>> - pre_finalize=pre_finalize,
>>> - cancel=config.cancel)
>>> + job_runner = TestJobRunner(vm, job['id'],
>>> + auto_dismiss=job['auto-dismiss'],
>>> + auto_finalize=job['auto-finalize'],
>>> + cancel=config.cancel)
>>> + job_runner.run()
>>> log('')
>>>
>>>
>>> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
>>> index 3390fab021..37a8b4d649 100644
>>> --- a/tests/qemu-iotests/iotests.py
>>> +++ b/tests/qemu-iotests/iotests.py
>>> @@ -460,6 +460,130 @@ def remote_filename(path):
>>> else:
>>> raise Exception("Protocol %s not supported" % (imgproto))
>>>
>>> +
>>> +class JobRunner:
>>
>> [...]
>>
>>> + def on_ready(self, event):
>>> + if self.logging:
>>> + self._vm.qmp_log('job-complete', id=self._id)
>>> + else:
>>> + self._vm.qmp('job-complete', id=self._id)
>>
>> I suppose this is a bug fix. (The old version always called qmp_log.)
>>
>
> Technically yes. It was needed for 040.
>
>> But what about adding a do_qmp method to JobRunner that does the
>> “if self.logging { self._vm.qmp_log() } else { self._vm.qmp }” part so
>> we don’t have to inline that everywhere?
>>
>> Max
>>
>
> I'll just clean up the logging series I had to do it at a more
> fundamental level.
OK. So you’re looking to basically get VM.qmp() to log automatically if
necessary? (or maybe qmp_log() to not log unless necessary)
Max
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 1/2] iotests: add JobRunner class
2020-02-27 11:44 ` Max Reitz
@ 2020-03-03 21:32 ` John Snow
0 siblings, 0 replies; 9+ messages in thread
From: John Snow @ 2020-03-03 21:32 UTC (permalink / raw)
To: Max Reitz, qemu-devel; +Cc: Kevin Wolf, qemu-block
On 2/27/20 6:44 AM, Max Reitz wrote:
>> I'll just clean up the logging series I had to do it at a more
>> fundamental level.
> OK. So you’re looking to basically get VM.qmp() to log automatically if
> necessary? (or maybe qmp_log() to not log unless necessary)
>
> Max
>
Yes. If this series looks good I will just rebase it on top of the
logging series. Then self.logging goes away and the calls to qmp_log et
al just become unconditional.
Then we can enable/disable "all QMP logging" globally instead of
individually throughout the iotests shared codebase.
--js
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2020-03-03 21:33 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-02-26 0:44 [PATCH 0/2] iotests: complicate run_job with this one weird trick? John Snow
2020-02-26 0:44 ` [PATCH 1/2] iotests: add JobRunner class John Snow
2020-02-26 11:18 ` Max Reitz
2020-02-26 17:58 ` John Snow
2020-02-27 11:44 ` Max Reitz
2020-03-03 21:32 ` John Snow
2020-02-26 0:44 ` [PATCH 2/2] iotests: modify test 040 to use JobRunner John Snow
2020-02-26 11:31 ` Max Reitz
2020-02-26 18:04 ` John Snow
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.