1+ package org .tron .core .utils ;
2+
3+ import static org .junit .Assert .assertEquals ;
4+ import static org .junit .Assert .assertFalse ;
5+ import static org .junit .Assert .assertThrows ;
6+ import static org .junit .Assert .assertTrue ;
7+ import static org .mockito .Mockito .mockConstruction ;
8+ import static org .mockito .Mockito .when ;
9+
10+ import java .util .Collections ;
11+ import java .util .HashSet ;
12+ import java .util .concurrent .atomic .AtomicBoolean ;
13+ import java .util .concurrent .atomic .AtomicInteger ;
14+ import org .junit .After ;
15+ import org .junit .Before ;
16+ import org .junit .Test ;
17+ import org .junit .runner .RunWith ;
18+ import org .mockito .MockedConstruction ;
19+ import org .mockito .junit .MockitoJUnitRunner ;
20+ import org .reflections .Reflections ;
21+ import org .tron .core .actuator .AbstractActuator ;
22+ import org .tron .core .actuator .TransferActuator ;
23+ import org .tron .core .config .args .Args ;
24+ import org .tron .core .exception .TronError ;
25+
26+ @ RunWith (MockitoJUnitRunner .class )
27+ public class TransactionRegisterTest {
28+
29+ @ Before
30+ public void init () {
31+ Args .getInstance ().setActuatorSet (new HashSet <>());
32+ TransactionRegister .resetForTesting ();
33+ }
34+
35+ @ After
36+ public void destroy () {
37+ Args .clearParam ();
38+ }
39+
40+ @ Test
41+ public void testAlreadyRegisteredSkipRegistration () {
42+ TransactionRegister .registerActuator ();
43+ assertTrue ("First registration should be completed" , TransactionRegister .isRegistered ());
44+
45+ TransactionRegister .registerActuator ();
46+ assertTrue ("Registration should still be true" , TransactionRegister .isRegistered ());
47+ }
48+
49+ @ Test
50+ public void testConcurrentAccessThreadSafe () throws InterruptedException {
51+ final int threadCount = 5 ;
52+ Thread [] threads = new Thread [threadCount ];
53+ final AtomicBoolean testPassed = new AtomicBoolean (true );
54+
55+ for (int i = 0 ; i < threadCount ; i ++) {
56+ threads [i ] = new Thread (() -> {
57+ try {
58+ TransactionRegister .registerActuator ();
59+ } catch (Throwable e ) {
60+ testPassed .set (false );
61+ }
62+ });
63+ }
64+
65+ for (Thread thread : threads ) {
66+ thread .start ();
67+ }
68+
69+ for (Thread thread : threads ) {
70+ thread .join ();
71+ }
72+
73+ assertTrue ("All threads should complete without exceptions" , testPassed .get ());
74+ assertTrue ("Registration should be completed" , TransactionRegister .isRegistered ());
75+ }
76+
77+ @ Test
78+ public void testDoubleCheckLockingAtomicBoolean () {
79+ assertFalse ("Initial registration state should be false" , TransactionRegister .isRegistered ());
80+
81+ TransactionRegister .registerActuator ();
82+ assertTrue ("After first call, should be registered" , TransactionRegister .isRegistered ());
83+
84+ TransactionRegister .registerActuator ();
85+ assertTrue ("After second call, should still be registered" , TransactionRegister .isRegistered ());
86+ }
87+
88+ @ Test
89+ public void testRegistrationRunsExactlyOnce () {
90+ final AtomicInteger constructorCallCount = new AtomicInteger (0 );
91+
92+ try (MockedConstruction <Reflections > ignored = mockConstruction (Reflections .class ,
93+ (mock , context ) -> {
94+ constructorCallCount .incrementAndGet ();
95+ when (mock .getSubTypesOf (AbstractActuator .class )).thenReturn (Collections .emptySet ());
96+ })) {
97+
98+ // Call multiple times; Reflections should only be constructed once
99+ for (int i = 0 ; i < 5 ; i ++) {
100+ TransactionRegister .registerActuator ();
101+ }
102+
103+ assertEquals ("Reflections should be constructed exactly once regardless of call count" ,
104+ 1 , constructorCallCount .get ());
105+ assertTrue (TransactionRegister .isRegistered ());
106+ }
107+ }
108+
109+ @ Test
110+ public void testMultipleCallsConsistency () {
111+ assertFalse ("Should start unregistered" , TransactionRegister .isRegistered ());
112+
113+ TransactionRegister .registerActuator ();
114+ assertTrue ("Should be registered after first call" , TransactionRegister .isRegistered ());
115+
116+ for (int i = 0 ; i < 5 ; i ++) {
117+ TransactionRegister .registerActuator ();
118+ assertTrue ("Should remain registered after call " + (i + 2 ),
119+ TransactionRegister .isRegistered ());
120+ }
121+ }
122+
123+ @ Test
124+ public void testThrowsTronError () {
125+ try (MockedConstruction <Reflections > ignored = mockConstruction (Reflections .class ,
126+ (mock , context ) -> when (mock .getSubTypesOf (AbstractActuator .class ))
127+ .thenReturn (Collections .singleton (TransferActuator .class )));
128+ MockedConstruction <TransferActuator > ignored1 = mockConstruction (TransferActuator .class ,
129+ (mock , context ) -> {
130+ throw new RuntimeException ("boom" );
131+ })) {
132+ TronError error = assertThrows (TronError .class , TransactionRegister ::registerActuator );
133+ assertEquals (TronError .ErrCode .ACTUATOR_REGISTER , error .getErrCode ());
134+ assertTrue (error .getMessage ().contains ("TransferActuator" ));
135+ }
136+ }
137+ }
0 commit comments