app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
← Commit history
Merge PR #344: Mechanical BOM that reads a large real assembly right: views, make/buy reasons, kits, libraries, wires, cut and order lists (feature/bom-v2 -> master) merge
Showing changes introduced by the merge (against its first parent).
2 files changed
+101
tests/test_mechanical.py+86@@ -264,4 +264,90 @@ class Readbacks(unittest.TestCase): scoped = purchased.material_list(app, {'query': 'steel', 'libraryId': 'lib-fusion'})['materials'] self.assertEqual([r['name'] for r in scoped], ['Steel']) ++class ReferencedOccurrences(unittest.TestCase):+ def test_suppression_inside_a_referenced_design_is_decided_in_its_source(self):+ class Proxy(Entity):+ @property+ def timelineObject(self):+ raise RuntimeError('2 : InternalValidationError : dcInstance')+ ref_root = Entity(isReferencedComponent=True, assemblyContext=None)+ inner = Proxy(isReferencedComponent=False, assemblyContext=ref_root)+ self.assertIsNone(c.occurrence_suppression(inner))+ stray = Proxy(isReferencedComponent=False, assemblyContext=None)+ with self.assertRaises(RuntimeError):+ c.occurrence_suppression(stray)+ self.assertTrue(c.occurrence_suppression(Entity(timelineObject=NS(isSuppressed=True))))+++class NativeProperties(unittest.TestCase):+ """partNumber and description are cloud reads (~0.6 s each per component on a cloud document)."""+ def setUp(self):+ c._entities.clear()+ self.astate = importlib.import_module('mechanical_test_commands.mechanical.assembly_state')+ self.astate._native_properties.clear()++ def component(self, reads):+ class Comp(Entity):+ @property+ def partNumber(self):+ reads.append('pn'); return 'ADM-1'+ @property+ def description(self):+ reads.append('desc'); return 'Bracket'+ return Comp(objectType='adsk::fusion::Component', name='Bracket', revisionId='r1', attributes=Collection(), material=None, entityToken='tok-bracket')++ def test_the_revision_witness_never_reads_them(self):+ reads = []+ comp = self.component(reads)+ row = self.astate.metadata(comp, native=False)+ self.assertEqual((row['partNumber'], row['description'], reads), (None, None, []))++ def test_they_are_read_once_per_component_revision(self):+ reads = []+ comp = self.component(reads)+ self.assertEqual(self.astate.metadata(comp)['partNumber'], 'ADM-1')+ self.astate.metadata(comp)+ self.assertEqual(reads, ['pn', 'desc'])+ comp.revisionId = 'r2'+ self.astate.metadata(comp)+ self.assertEqual(reads, ['pn', 'desc', 'pn', 'desc'])+++class IdentityRegistry(unittest.TestCase):+ """2026-10-28: every lookup compared against every identity in the session, so the revision of a+ 1,249-component assembly ran for many minutes. Lookups now compare a few candidates."""+ def setUp(self):+ c._entities.clear()++ def entity(self, name, token, owner="Owner"):+ return Entity(objectType='adsk::fusion::BRepBody', name=name, entityToken=token, parentComponent=NS(name=owner))++ def test_same_object_same_identity_and_same_names_stay_distinct(self):+ a, b = self.entity('Body1', 't-a'), self.entity('Body1', 't-b')+ self.assertEqual(c.identity(a), c.identity(a))+ self.assertNotEqual(c.identity(a), c.identity(b))++ def test_a_renamed_entity_is_found_by_its_token(self):+ a = self.entity('Body1', 't-a')+ key = c.identity(a)+ a.name = 'Renamed'+ self.assertEqual(c.identity(a), key)++ def test_a_reset_registry_forgets_its_indexes(self):+ a = self.entity('Body1', 't-a')+ key = c.identity(a)+ c._entities.clear()+ self.assertNotEqual(c.identity(a), key)++ def test_a_lookup_compares_a_few_candidates_not_the_session(self):+ for i in range(3000):+ c.identity(self.entity('Body%d' % i, 't-%d' % i, owner='C%d' % (i % 50)))+ probe = self.entity('Body1500', 't-new', owner='C0')+ calls = []+ real = c.same+ with patch.object(c, 'same', side_effect=lambda x, y: calls.append(1) or real(x, y)):+ c.identity(probe)+ self.assertLess(len(calls), 10)+ if __name__=='__main__':unittest.main()
tests/test_mechanical_revision.py+15@@ -178,6 +178,21 @@ class RevisionInitialization(unittest.TestCase): self.assertEqual(error.exception.code, 'revision_unavailable') self.assertEqual(self.events, []) + def test_a_component_inserted_by_reference_enters_by_its_source_version(self):+ # 2026-10-28: a production cell inserts 23 designs by reference; every operation refused+ source = Entity(objectType='adsk::core::Document', name='Referenced', dataFile=NS(id='urn:ref', versionNumber=7))+ self.part.parentDesign = Entity(parentDocument=source)+ first = c.revision(self.design)+ self.assertNotIn('Part', {row[1] for row in self.events if row[0] == 'mesh'})+ self.assertEqual(c.revision(self.design), first)+ source.dataFile = NS(id='urn:ref', versionNumber=8)+ self.assertNotEqual(c.revision(self.design), first)+ # a foreign component from an unsaved document is not a reference: still refused+ self.part.parentDesign = Entity(parentDocument=Entity(dataFile=None))+ with self.assertRaises(c.MechanicalError) as error:+ c.revision(self.design)+ self.assertEqual(error.exception.code, 'revision_unavailable')+ def test_invalid_or_malformed_mesh_collection_never_counts_as_empty(self): for meshes in (None, NS(count=0, isValid=False, objectType='adsk::fusion::MeshBodies'), NS(count=0, isValid=True, objectType='adsk::fusion::BRepBodies'), NS(count=0)):