@@ -494,3 +494,102 @@ end
494494 ex2 = Expression (+ (x1, x2, x3); operators)
495495 @test string (ex2) == " +(x1, x2, x3)"
496496end
497+
498+ @testitem " Node arbitrary-degree child access & mutation" begin
499+ using DynamicExpressions. NodeModule:
500+ Node, get_child, get_children, set_child!, set_children!
501+ using Test
502+
503+ # Build a tiny 3-ary tree: +(1, 2, 3)
504+ c1 = Node {Float64,3} (; val= 1.0 )
505+ c2 = Node {Float64,3} (; val= 2.0 )
506+ c3 = Node {Float64,3} (; val= 3.0 )
507+ p = Node {Float64,3} (; op= 1 , children= (c1, c2, c3))
508+
509+ @test p isa Node{Float64,3 } # new type parameter D shows up
510+ @test get_children (p) == (c1, c2, c3)
511+ @test get_child (p, 2 ) === c2
512+ @test get_children (p, Val (1 )) === (c1,) # Val-specialised accessor
513+ @test_throws BoundsError get_child (p, 4 )
514+
515+ # Replace the 3rd child in-place
516+ new_c3 = Node {Float64,3} (; val= 30.0 )
517+ set_child! (p, new_c3, 3 )
518+ @test get_child (p, 3 ) === new_c3
519+
520+ # Length mismatch is allowed; it will be poisoned at the end
521+ set_children! (p, (c1, c2))
522+ @test get_child (p, 2 ) === c2
523+
524+ # Poison node is just self
525+ @test get_child (p, 3 ) === p
526+ end
527+
528+ @testitem " Node property forwarding (`.l` / `.r`) still works" begin
529+ using DynamicExpressions. NodeModule: Node
530+ using Test
531+
532+ a = Node {Float64,2} (; val= 10.0 )
533+ b = Node {Float64,2} (; val= 20.0 )
534+ root = Node {Float64,2} (; op= 1 , children= (a, b))
535+
536+ @test root. l === a
537+ @test root. r === b
538+
539+ new_a = Node {Float64,2} (; val= 99.0 )
540+ root. l = new_a
541+ @test root. l === new_a
542+ end
543+
544+ @testitem " Constructor guards all-defaults call throws" begin
545+ using DynamicExpressions: AbstractExpressionNode
546+ using Test
547+
548+ mutable struct MyBadNode{T,D} <: AbstractExpressionNode{T,D}
549+ degree:: UInt8
550+ constant:: Bool
551+ val:: T
552+ feature:: UInt16
553+ op:: UInt8
554+ children:: NTuple{D,MyBadNode{T,D}}
555+ end
556+
557+ # Calling with *nothing* default should now error
558+ @test_throws " Did you forget to define" MyBadNode {Float64,3} ()
559+
560+ mutable struct MyBadNode2{T,D} <: AbstractExpressionNode{T,D}
561+ degree:: UInt8
562+ constant:: Bool
563+ val:: T
564+ feature:: UInt16
565+ op:: UInt8
566+ children:: NTuple{D,MyBadNode2{T,D}}
567+
568+ MyBadNode2 {T,D} () where {T,D} = new {T,D} ()
569+ end
570+
571+ # We only need this; it will default to 2 for D anyways:
572+ node = MyBadNode2 {Float64} ()
573+ @test typeof (node) <: MyBadNode2{Float64,2}
574+ end
575+
576+ @testitem " branch_copy_into! copies generic children" begin
577+ using DynamicExpressions. NodeModule: Node, get_children
578+ using DynamicExpressions. NodePreallocationModule: branch_copy_into!
579+ using Test
580+
581+ operators = OperatorEnum (2 => (+ , - , * ))
582+ leaves = ntuple (i -> Node {Float64,3} (; val= i), 3 )
583+ src = Node {Float64,3} (; op= 1 , children= leaves)
584+ dummy = Node {Float64,3} (;
585+ op= 2 ,
586+ children= (
587+ Node {Float64,3} (; val= 0 ), Node {Float64,3} (; val= 0 ), Node {Float64,3} (; val= 0 )
588+ ),
589+ )
590+
591+ branch_copy_into! (dummy, src, get_children (src)... )
592+
593+ @test dummy. op == src. op
594+ @test get_children (dummy) == get_children (src)
595+ end
0 commit comments