Add Chromium-only Blender WebEngine parity work
This commit is contained in:
237
blender-5.2.0/tests/python/mesh_validate.py
Normal file
237
blender-5.2.0/tests/python/mesh_validate.py
Normal file
@@ -0,0 +1,237 @@
|
||||
# SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
import unittest
|
||||
import bpy
|
||||
import sys
|
||||
import pathlib
|
||||
|
||||
"""
|
||||
blender -b --factory-startup --python tests/python/mesh_validate.py -- --testdir tests/files/
|
||||
"""
|
||||
|
||||
|
||||
class TestMeshValidate(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
if bpy.context.object and bpy.context.object.mode != 'OBJECT':
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.object.delete()
|
||||
|
||||
for mesh in list(bpy.data.meshes):
|
||||
bpy.data.meshes.remove(mesh)
|
||||
|
||||
def tearDown(self):
|
||||
for mesh in list(bpy.data.meshes):
|
||||
bpy.data.meshes.remove(mesh)
|
||||
|
||||
def test_invalid_edge_vertex_indices(self):
|
||||
verts = [(0, 0, 0), (1, 1, 1)]
|
||||
edges = [(0, 99)] # Invalid vertex 99
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, edges, [])
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_duplicate_edge_vertex_indices(self):
|
||||
verts = [(0, 0, 0), (1, 1, 1)]
|
||||
edges = [(0, 0)] # Invalid edge from a vertex to itself
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, edges, [])
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_bad_face_offsets(self):
|
||||
bpy.ops.mesh.primitive_cube_add()
|
||||
mesh = bpy.context.active_object.data
|
||||
|
||||
mesh.polygons[0].loop_start = 100
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_bad_material_indices(self):
|
||||
bpy.ops.mesh.primitive_plane_add()
|
||||
obj = bpy.context.active_object
|
||||
mesh = obj.data
|
||||
|
||||
attr = mesh.attributes.new(name="material_index", type='INT', domain='FACE')
|
||||
attr.data[0].value = -4
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_duplicate_faces(self):
|
||||
verts = [(0, 0, 0), (1, 0, 0), (1, 1, 0)]
|
||||
faces = [(0, 1, 2), (2, 0, 1)]
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_invalid_float_attributes(self):
|
||||
bpy.ops.mesh.primitive_plane_add()
|
||||
mesh = bpy.context.active_object.data
|
||||
|
||||
mesh.vertices[0].co.x = float('nan')
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_duplicate_edges(self):
|
||||
verts = [(0, 0, 0), (1, 1, 1)]
|
||||
edges = [(0, 1), (1, 0)]
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, edges, [])
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_faces_with_bad_edge_references(self):
|
||||
verts = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
|
||||
edges = []
|
||||
faces = [(0, 1, 2, 3)]
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
corner_edges = mesh.attributes[".corner_edge"].data
|
||||
corner_edges[2].value = 0
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_negative_intermediate_face_offset(self):
|
||||
# An intermediate face's start offset is negative.
|
||||
verts = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0),
|
||||
(2, 0, 0), (3, 0, 0), (3, 1, 0), (2, 1, 0)]
|
||||
edges = []
|
||||
faces = [(0, 1, 2, 3), (4, 5, 6, 7)]
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
mesh.polygons[1].loop_start = -4
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_intermediate_face_offset_past_corners(self):
|
||||
# An intermediate face's start offset is beyond the number of corners.
|
||||
verts = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0),
|
||||
(2, 0, 0), (3, 0, 0), (3, 1, 0), (2, 1, 0)]
|
||||
edges = []
|
||||
faces = [(0, 1, 2, 3), (4, 5, 6, 7)]
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
mesh.polygons[1].loop_start = 1000
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_corner_edge_to_duplicate_edge(self):
|
||||
# A corner referencing a duplicate edge that gets removed must be
|
||||
# redirected to the kept edge.
|
||||
verts = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
|
||||
edges = [(0, 1), (1, 2), (2, 3), (3, 0), (0, 1)]
|
||||
faces = [(0, 1, 2, 3)]
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, edges, faces)
|
||||
|
||||
# Point the first corner's edge at the duplicate.
|
||||
corner_edges = mesh.attributes[".corner_edge"].data
|
||||
corner_edges[0].value = 4
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_bad_corner_edge_after_bad_vert_edge(self):
|
||||
# When earlier edges have been removed (e.g. for out-of-range verts), the
|
||||
# index used by the bad-edge fix-up must adapt.
|
||||
verts = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
|
||||
faces = [(0, 1, 2, 3)]
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
|
||||
# Give an edge an out-of-range vertex so it gets removed by validation.
|
||||
mesh.attributes[".edge_verts"].data[0].value = (0, 99)
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_missing_edge_with_oob_corner_edge(self):
|
||||
# A face with a missing edge and an out-of-range corner_edge index.
|
||||
verts = [(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]
|
||||
faces = [(0, 1, 2, 3)]
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
|
||||
mesh.attributes[".edge_verts"].data[0].value = (0, 99)
|
||||
mesh.attributes[".corner_edge"].data[0].value = 999
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
def test_overlapping_face_offsets(self):
|
||||
# Non-monotonic face offsets.
|
||||
verts = [(float(i), 0.0, 0.0) for i in range(12)]
|
||||
faces = [(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)]
|
||||
|
||||
mesh = bpy.data.meshes.new("test_mesh")
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
|
||||
mesh.polygons[1].loop_start = 5
|
||||
mesh.polygons[2].loop_start = 3
|
||||
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertEqual(len(mesh.polygons), 0)
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
|
||||
args = None
|
||||
|
||||
|
||||
class TestMeshValidateExistingFile(unittest.TestCase):
|
||||
def setUp(self):
|
||||
bpy.ops.wm.open_mainfile(filepath=str(args.testdir / "sculpting/invalid_mdisp_cube.blend"), load_ui=False)
|
||||
|
||||
def test_mesh_with_bad_multires_displacements(self):
|
||||
mesh = bpy.context.active_object.data
|
||||
self.assertTrue(mesh.validate(verbose=True))
|
||||
self.assertFalse(mesh.validate(verbose=True))
|
||||
|
||||
|
||||
def main():
|
||||
global args
|
||||
import argparse
|
||||
|
||||
argv = [sys.argv[0]]
|
||||
if '--' in sys.argv:
|
||||
argv += sys.argv[sys.argv.index('--') + 1:]
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--testdir', required=True, type=pathlib.Path)
|
||||
|
||||
args, remaining = parser.parse_known_args(argv)
|
||||
|
||||
unittest.main(argv=remaining)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user