test_paths.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import shutil
  2. import subprocess
  3. import sys
  4. import tempfile
  5. import unittest
  6. import wave
  7. from pathlib import Path
  8. SCRIPTS = (
  9. Path(__file__).resolve().parents[2]
  10. / "skills/proving-it-works-with-a-movie/scripts"
  11. )
  12. sys.path.insert(0, str(SCRIPTS))
  13. import media_paths
  14. class MediaPathRegression(unittest.TestCase):
  15. def test_frame_staging_uses_ordinary_ordered_files(self):
  16. with tempfile.TemporaryDirectory() as directory:
  17. root = Path(directory)
  18. source = root / "movie O'Brien λ & [take]"
  19. source.mkdir()
  20. (source / "b.png").write_bytes(b"second")
  21. (source / "a.png").write_bytes(b"first")
  22. staged = media_paths.stage_frames(source, root / "staged")
  23. self.assertEqual(
  24. [path.read_bytes() for path in staged],
  25. [b"first", b"second"],
  26. )
  27. self.assertTrue(all(not path.is_symlink() for path in staged))
  28. self.assertEqual(len(list(source.iterdir())), 2)
  29. def test_frame_staging_rejects_an_empty_source(self):
  30. with tempfile.TemporaryDirectory() as directory:
  31. root = Path(directory)
  32. source = root / "empty"
  33. source.mkdir()
  34. with self.assertRaisesRegex(ValueError, "no PNG frames"):
  35. media_paths.stage_frames(source, root / "staged")
  36. self.assertFalse((root / "staged").exists())
  37. def test_ffconcat_entry_is_accepted_by_ffmpeg(self):
  38. ffmpeg = shutil.which("ffmpeg")
  39. if ffmpeg is None:
  40. self.skipTest("required executable not on PATH: ffmpeg")
  41. with tempfile.TemporaryDirectory() as directory:
  42. root = Path(directory) / "movie O'Brien λ & [take]"
  43. nested = root / "subtitles" / "nested"
  44. nested.mkdir(parents=True)
  45. audio = nested / "tone.wav"
  46. with wave.open(str(audio), "wb") as output:
  47. output.setnchannels(1)
  48. output.setsampwidth(2)
  49. output.setframerate(8000)
  50. output.writeframes(b"\x00\x00" * 800)
  51. if sys.platform == "win32":
  52. self.assertRegex(str(audio.resolve()), r"^[A-Za-z]:\\")
  53. listing = root / "concat.txt"
  54. listing.write_text(
  55. "ffconcat version 1.0\n" + media_paths.ffconcat_entry(audio),
  56. encoding="utf-8",
  57. )
  58. result = subprocess.run(
  59. [
  60. ffmpeg,
  61. "-nostdin",
  62. "-v",
  63. "error",
  64. "-f",
  65. "concat",
  66. "-safe",
  67. "0",
  68. "-i",
  69. str(listing),
  70. "-f",
  71. "null",
  72. "-",
  73. ],
  74. cwd=Path(directory),
  75. capture_output=True,
  76. timeout=30,
  77. )
  78. self.assertEqual(
  79. result.returncode,
  80. 0,
  81. (result.stdout + result.stderr).decode("utf-8", errors="replace"),
  82. )
  83. def test_ffconcat_entry_rejects_line_breaks(self):
  84. with self.assertRaisesRegex(ValueError, "cannot contain line breaks"):
  85. media_paths.ffconcat_entry(Path("bad\nname.wav"))
  86. if __name__ == "__main__":
  87. unittest.main()