Files
workinf_Blender_Wasm/blender-5.2.0/tests/python/bl_blendfile_autosave.py
2026-08-12 04:47:48 -04:00

263 lines
9.7 KiB
Python

# SPDX-FileCopyrightText: 2026 Blender Authors
#
# SPDX-License-Identifier: Apache-2.0
import unittest
# ./blender.bin --background --python tests/python/bl_blendfile_autosave.py -- --output-dir=/tmp/
import bpy
import os
import re
import sys
import tempfile
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from bl_blendfile_utils import TestHelper
class TestBlendFileAutosave(TestHelper):
def __init__(self, args):
super().__init__(args)
@staticmethod
def find_autosave_path(base_dir, base_filename):
pattern = re.compile("{}_[0-9]+_autosave.blend".format(base_filename))
paths = os.listdir(base_dir)
for path in paths:
if pattern.match(path):
return os.path.join(base_dir, path)
return None
def test_autosave_restore(self):
bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
orig_mesh = bpy.data.meshes.new("OrigCubeMesh")
orig_mesh.use_fake_user = True
output_dir = self.args.output_dir
self.ensure_path(output_dir)
bpy.context.preferences.filepaths.temporary_directory = output_dir
output_path = os.path.join(output_dir, "blendfile_autosave.blend")
orig_data = self.blender_data_to_tuple(bpy.data, "orig_data")
bpy.ops.wm.save_as_mainfile(filepath=output_path, check_existing=False, compress=False)
new_mesh = bpy.data.meshes.new("NewCubeMesh")
new_mesh.use_fake_user = True
autosave_data = self.blender_data_to_tuple(bpy.data, "autosave_data")
bpy.ops.wm.save_auto_save()
bpy.ops.wm.open_mainfile(filepath=output_path, load_ui=False)
read_data = self.blender_data_to_tuple(bpy.data, "read_data")
self.assertEqual(read_data, orig_data)
self.assertNotEqual(read_data, autosave_data)
self.assertIn("OrigCubeMesh", bpy.data.meshes)
self.assertNotIn("NewCubeMesh", bpy.data.meshes)
autosave_path = self.find_autosave_path(output_dir, "blendfile_autosave")
self.assertTrue(autosave_path)
self.assertNotEqual(output_path, autosave_path)
retval = bpy.ops.wm.recover_auto_save(filepath=autosave_path)
self.assertEqual(retval, {'FINISHED'})
recover_data = self.blender_data_to_tuple(bpy.data, "recover_data")
self.assertEqual(recover_data, autosave_data)
self.assertIn("OrigCubeMesh", bpy.data.meshes)
self.assertIn("NewCubeMesh", bpy.data.meshes)
class TestBlendFileImageAutosave(TestHelper):
def __init__(self, args):
super().__init__(args)
def setUpClass(self):
self.ensure_path(self.args.output_dir)
self.temp_dir = tempfile.TemporaryDirectory(dir=self.args.output_dir)
def tearDownClass(self):
self.temp_dir.cleanup()
@staticmethod
def find_autosave_path(base_dir, base_filename):
pattern = re.compile("{}_[0-9]+_autosave.blend".format(base_filename))
paths = os.listdir(base_dir)
for path in paths:
if pattern.match(path):
return os.path.join(base_dir, path)
return None
@staticmethod
def modify_image(image):
width, height = image.size
color = (1.0, 1.0, 1.0, 1.0)
pixels = width * height
for pixel in range(pixels):
for channel in range(4):
image.pixels[pixel * 4 + channel] = color[channel]
def check_image(self, image, expected_color):
width, height = image.size
pixels = width * height
actual_data = list(image.pixels[:])
expected_data = [0.0] * pixels * 4
for pixel in range(pixels):
for channel in range(4):
expected_data[pixel * 4 + channel] = expected_color[channel]
self.assertEqual(actual_data, expected_data)
def test_generated_image_restore(self):
self.ensure_path(self.args.output_dir)
with tempfile.TemporaryDirectory(dir=self.args.output_dir) as output_dir:
bpy.context.preferences.filepaths.temporary_directory = output_dir
output_path = os.path.join(output_dir, "blendfile_autosave_generated_image.blend")
# Immediately create the file so we can ensure we're working on a
# "generated" and not packed image with autosave
bpy.ops.wm.save_as_mainfile(filepath=output_path, check_existing=False, compress=False)
orig_image = bpy.data.images.new("GeneratedImage", 2, 2, alpha=True)
orig_image.use_fake_user = True
self.modify_image(orig_image)
self.check_image(orig_image, (1.0, 1.0, 1.0, 1.0))
bpy.ops.wm.save_auto_save()
bpy.ops.wm.open_mainfile(filepath=output_path, load_ui=False)
# File should not have image, only autosave should have image
self.assertNotIn("GeneratedImage", bpy.data.images)
autosave_path = self.find_autosave_path(output_dir, "blendfile_autosave_generated_image")
self.assertTrue(autosave_path)
self.assertNotEqual(output_path, autosave_path)
retval = bpy.ops.wm.recover_auto_save(filepath=autosave_path)
self.assertEqual(retval, {'FINISHED'})
self.assertIn("GeneratedImage", bpy.data.images)
self.check_image(bpy.data.images["GeneratedImage"], (1.0, 1.0, 1.0, 1.0))
bpy.ops.wm.open_mainfile(filepath=output_path, load_ui=False)
self.assertNotIn("GeneratedImage", bpy.data.images)
def test_packed_image_restore(self):
self.ensure_path(self.args.output_dir)
with tempfile.TemporaryDirectory(dir=self.args.output_dir) as output_dir:
bpy.context.preferences.filepaths.temporary_directory = output_dir
input_blendfile_path = os.path.join(self.args.src_test_dir, "autosave_image_test.blend")
bpy.ops.wm.open_mainfile(filepath=input_blendfile_path)
output_path = os.path.join(output_dir, "blendfile_autosave_packed_image.blend")
bpy.ops.wm.save_as_mainfile(filepath=output_path, check_existing=False, compress=False)
self.modify_image(bpy.data.images["PackedImage"])
self.check_image(bpy.data.images["PackedImage"], (1.0, 1.0, 1.0, 1.0))
bpy.ops.wm.save_auto_save()
bpy.ops.wm.open_mainfile(filepath=output_path, load_ui=False)
self.assertIn("PackedImage", bpy.data.images)
self.check_image(bpy.data.images["PackedImage"], (0.0, 0.0, 0.0, 1.0))
autosave_path = self.find_autosave_path(output_dir, "blendfile_autosave_packed_image")
self.assertTrue(autosave_path)
self.assertNotEqual(output_path, autosave_path)
retval = bpy.ops.wm.recover_auto_save(filepath=autosave_path)
self.assertEqual(retval, {'FINISHED'})
# Loading the autosave file should show the "temporary" changes
self.assertIn("PackedImage", bpy.data.images)
self.check_image(bpy.data.images["PackedImage"], (1.0, 1.0, 1.0, 1.0))
def test_external_image_restore(self):
self.ensure_path(self.args.output_dir)
with tempfile.TemporaryDirectory(dir=self.args.output_dir) as output_dir:
bpy.context.preferences.filepaths.temporary_directory = output_dir
input_blendfile_path = os.path.join(self.args.src_test_dir, "autosave_image_test.blend")
bpy.ops.wm.open_mainfile(filepath=input_blendfile_path)
output_path = os.path.join(output_dir, "blendfile_autosave_external_image.blend")
bpy.ops.wm.save_as_mainfile(filepath=output_path, check_existing=False, compress=False)
self.modify_image(bpy.data.images["ExternalImage"])
self.check_image(bpy.data.images["ExternalImage"], (1.0, 1.0, 1.0, 1.0))
bpy.ops.wm.save_auto_save()
bpy.ops.wm.open_mainfile(filepath=output_path, load_ui=False)
self.assertIn("ExternalImage", bpy.data.images)
self.check_image(bpy.data.images["ExternalImage"], (0.0, 0.0, 0.0, 1.0))
autosave_path = self.find_autosave_path(output_dir, "blendfile_autosave_external_image")
self.assertTrue(autosave_path)
self.assertNotEqual(output_path, autosave_path)
retval = bpy.ops.wm.recover_auto_save(filepath=autosave_path)
self.assertEqual(retval, {'FINISHED'})
# Loading the autosave file should show the "temporary" changes
self.assertIn("ExternalImage", bpy.data.images)
self.check_image(bpy.data.images["ExternalImage"], (1.0, 1.0, 1.0, 1.0))
# Loading the actual file should show the original data
bpy.data.images["ExternalImage"].reload()
self.check_image(bpy.data.images["ExternalImage"], (0.0, 0.0, 0.0, 1.0))
return
TESTS = (
TestBlendFileAutosave,
TestBlendFileImageAutosave
)
def argparse_create():
import argparse
# When --help or no args are given, print this help
description = "Test basic IO of autosaving a .blend file."
parser = argparse.ArgumentParser(description=description)
parser.add_argument(
"--src-test-dir",
dest="src_test_dir",
help="Root tests directory to search for blendfiles",
required=True,
)
parser.add_argument(
"--output-dir",
dest="output_dir",
help="Where to output temp saved files",
required=True,
)
return parser
def main():
args = argparse_create().parse_args()
# Don't write thumbnails into the home directory.
bpy.context.preferences.filepaths.file_preview_type = 'NONE'
for Test in TESTS:
Test(args).run_all_tests()
if __name__ == '__main__':
import sys
sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
main()