Mercurial > ~astiob > upreckon > hgweb
comparison testcases.py @ 77:69eadc60f4e2
Memory limit is now applied to the RSS when os.wait4 is available
| author | Oleg Oshmyan <chortos@inbox.lv> |
|---|---|
| date | Thu, 13 Jan 2011 19:47:35 +0200 |
| parents | 0e5ae28e0b2b |
| children | ee8a99dcaaed |
comparison
equal
deleted
inserted
replaced
| 76:0e5ae28e0b2b | 77:69eadc60f4e2 |
|---|---|
| 1 #! /usr/bin/env python | 1 #! /usr/bin/env python |
| 2 # Copyright (c) 2010 Chortos-2 <chortos@inbox.lv> | 2 # Copyright (c) 2010-2011 Chortos-2 <chortos@inbox.lv> |
| 3 | 3 |
| 4 # TODO: copy the ansfile if not options.erase even if no validator is used | 4 # TODO: copy the ansfile if not options.erase even if no validator is used |
| 5 | 5 |
| 6 from __future__ import division, with_statement | 6 from __future__ import division, with_statement |
| 7 | 7 |
| 270 del kwargs['preexec_fn'] | 270 del kwargs['preexec_fn'] |
| 271 return call_real(*args, **kwargs) | 271 return call_real(*args, **kwargs) |
| 272 else: | 272 else: |
| 273 return call_real(*args, **kwargs) | 273 return call_real(*args, **kwargs) |
| 274 | 274 |
| 275 # Emulate memory limits on platforms compatible with 4.3BSD but not XSI | |
| 276 # I say 'emulate' because the OS will allow excessive memory usage | |
| 277 # anyway; Upreckon will just treat the test case as not passed. | |
| 278 # To do this, we not only require os.wait4 to be present but also | |
| 279 # assume things about the implementation of subprocess.Popen. | |
| 280 try: | |
| 281 def waitpid_emu(pid, options, _wait4=os.wait4): | |
| 282 global last_rusage | |
| 283 pid, status, last_rusage = _wait4(pid, options) | |
| 284 return pid, status | |
| 285 _waitpid = os.waitpid | |
| 286 os.waitpid = waitpid_emu | |
| 287 try: | |
| 288 defaults = Popen._internal_poll.__func__.__defaults__ | |
| 289 except AttributeError: | |
| 290 # Python 2.5 | |
| 291 defaults = Popen._internal_poll.im_func.func_defaults | |
| 292 i = defaults.index(_waitpid) | |
| 293 defaults = defaults[:i] + (waitpid_emu,) + defaults[i+1:] | |
| 294 try: | |
| 295 Popen._internal_poll.__func__.__defaults__ = defaults | |
| 296 except AttributeError: | |
| 297 pass | |
| 298 Popen._internal_poll.im_func.func_defaults = defaults | |
| 299 except (AttributeError, ValueError): | |
| 300 pass | |
| 275 | 301 |
| 276 __all__ = ('TestCase', 'load_problem', 'TestCaseNotPassed', | 302 __all__ = ('TestCase', 'load_problem', 'TestCaseNotPassed', |
| 277 'TimeLimitExceeded', 'CanceledByUser', 'WrongAnswer', | 303 'TimeLimitExceeded', 'CanceledByUser', 'WrongAnswer', |
| 278 'NonZeroExitCode', 'CannotStartTestee', | 304 'NonZeroExitCode', 'CannotStartTestee', |
| 279 'CannotStartValidator', 'CannotReadOutputFile', | 305 'CannotStartValidator', 'CannotReadOutputFile', |
| 283 | 309 |
| 284 # Exceptions | 310 # Exceptions |
| 285 | 311 |
| 286 class TestCaseNotPassed(Exception): __slots__ = () | 312 class TestCaseNotPassed(Exception): __slots__ = () |
| 287 class TimeLimitExceeded(TestCaseNotPassed): __slots__ = () | 313 class TimeLimitExceeded(TestCaseNotPassed): __slots__ = () |
| 314 class MemoryLimitExceeded(TestCaseNotPassed): __slots__ = () | |
| 288 class CanceledByUser(TestCaseNotPassed): __slots__ = () | 315 class CanceledByUser(TestCaseNotPassed): __slots__ = () |
| 289 | 316 |
| 290 class WrongAnswer(TestCaseNotPassed): | 317 class WrongAnswer(TestCaseNotPassed): |
| 291 __slots__ = 'comment' | 318 __slots__ = 'comment' |
| 292 def __init__(self, comment=''): | 319 def __init__(self, comment=''): |
| 537 except Exception: | 564 except Exception: |
| 538 # Well, at least we tried | 565 # Well, at least we tried |
| 539 pass | 566 pass |
| 540 case.open_infile() | 567 case.open_infile() |
| 541 case.time_started = None | 568 case.time_started = None |
| 569 global last_rusage | |
| 570 last_rusage = None | |
| 542 if case.problem.config.stdio: | 571 if case.problem.config.stdio: |
| 543 if options.erase and not case.validator or not case.problem.config.inname: | 572 if options.erase and not case.validator or not case.problem.config.inname: |
| 544 # TODO: re-use the same file name if possible | 573 # TODO: re-use the same file name if possible |
| 545 # FIXME: 2.5 lacks the delete parameter | 574 # FIXME: 2.5 lacks the delete parameter |
| 546 with tempfile.NamedTemporaryFile(delete=False) as f: | 575 with tempfile.NamedTemporaryFile(delete=False) as f: |
| 599 else: | 628 else: |
| 600 time_next_check = now + .15 | 629 time_next_check = now + .15 |
| 601 time.sleep(.001) | 630 time.sleep(.001) |
| 602 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0: | 631 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0: |
| 603 raise NonZeroExitCode(case.process.returncode) | 632 raise NonZeroExitCode(case.process.returncode) |
| 633 if case.maxmemory and last_rusage and last_rusage.ru_maxrss > case.maxmemory * (1024 if sys.platform != 'darwin' else 1048576): | |
| 634 raise MemoryLimitExceeded | |
| 604 callback() | 635 callback() |
| 605 case.has_called_back = True | 636 case.has_called_back = True |
| 606 outfile.seek(0) | 637 outfile.seek(0) |
| 607 return case.validate(outfile) | 638 return case.validate(outfile) |
| 608 else: | 639 else: |
| 651 else: | 682 else: |
| 652 time_next_check = now + .15 | 683 time_next_check = now + .15 |
| 653 time.sleep(.001) | 684 time.sleep(.001) |
| 654 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0: | 685 if config.globalconf.force_zero_exitcode and case.process.returncode or case.process.returncode < 0: |
| 655 raise NonZeroExitCode(case.process.returncode) | 686 raise NonZeroExitCode(case.process.returncode) |
| 687 if case.maxmemory and last_rusage and last_rusage.ru_maxrss > case.maxmemory * (1024 if sys.platform != 'darwin' else 1048576): | |
| 688 raise MemoryLimitExceeded | |
| 656 callback() | 689 callback() |
| 657 case.has_called_back = True | 690 case.has_called_back = True |
| 658 with open(case.problem.config.outname, 'rU') as output: | 691 with open(case.problem.config.outname, 'rU') as output: |
| 659 return case.validate(output) | 692 return case.validate(output) |
| 660 | 693 |
