blob: 093982e0a54434ab60e4fcd4c43927ce34fd437e [file]
#!/usr/bin/env vpython3
# Copyright 2026 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Unit tests for run_with_restat.py."""
import contextlib
import io
import os
import sys
import tempfile
import textwrap
import unittest
from unittest import mock
import run_with_restat
# Some arbitrary time in the past, in nanoseconds.
OLD_MTIME_NS = 1_000_000_000 * 10**9
class RunWithRestatTest(unittest.TestCase):
def setUp(self):
tmpdir = tempfile.TemporaryDirectory()
self.addCleanup(tmpdir.cleanup)
self.root = tmpdir.name
self.output = os.path.join(self.root, 'output.ts')
def run_wrapper(self, *argv):
"""Runs run_with_restat.main() and returns its exit code."""
with mock.patch.object(sys, 'argv', ['run_with_restat.py', *argv]):
with self.assertRaises(SystemExit) as cm:
run_with_restat.main()
return cm.exception.code
def write_output(self, content):
with open(self.output, 'w', encoding='utf-8') as f:
f.write(content)
os.utime(self.output, ns=(OLD_MTIME_NS, OLD_MTIME_NS))
def write_script(self, name, body):
path = os.path.join(self.root, name)
with open(path, 'w', encoding='utf-8') as f:
f.write(textwrap.dedent(body))
return path
def test_restores_mtime_of_unchanged_output(self):
self.write_output('same')
script = self.write_script(
'generate.py', """
import sys
with open(sys.argv[1], 'w') as f:
f.write('same')
""")
code = self.run_wrapper(self.output, '--', script, self.output)
self.assertEqual(code, 0)
self.assertEqual(os.stat(self.output).st_mtime_ns, OLD_MTIME_NS)
def test_keeps_new_mtime_of_changed_output(self):
self.write_output('old')
script = self.write_script(
'generate.py', """
import sys
with open(sys.argv[1], 'w') as f:
f.write('new')
""")
code = self.run_wrapper(self.output, '--', script, self.output)
self.assertEqual(code, 0)
self.assertGreater(os.stat(self.output).st_mtime_ns, OLD_MTIME_NS)
with open(self.output, encoding='utf-8') as f:
self.assertEqual(f.read(), 'new')
def test_runs_python_scripts_with_the_running_interpreter(self):
script = self.write_script(
'generate.py', """
import sys
with open(sys.argv[1], 'w') as f:
f.write(sys.executable)
""")
code = self.run_wrapper(self.output, '--', script, self.output)
self.assertEqual(code, 0)
with open(self.output, encoding='utf-8') as f:
self.assertEqual(f.read(), sys.executable)
def test_propagates_the_exit_code_and_leaves_outputs_alone(self):
self.write_output('old')
script = self.write_script(
'generate.py', """
import sys
with open(sys.argv[1], 'w') as f:
f.write('partial')
sys.exit(3)
""")
code = self.run_wrapper(self.output, '--', script, self.output)
self.assertEqual(code, 3)
# A failed run neither restores nor touches timestamps.
self.assertGreater(os.stat(self.output).st_mtime_ns, OLD_MTIME_NS)
def test_rejects_a_missing_separator(self):
with contextlib.redirect_stderr(io.StringIO()) as stderr:
code = self.run_wrapper(self.output, sys.executable, '-c', 'pass')
self.assertEqual(code, 1)
self.assertIn('Usage:', stderr.getvalue())
if __name__ == '__main__':
unittest.main()