@@ -165,4 +165,180 @@ public void testAnimationCallbackFinish() {
165165 mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
166166 verifyNoMoreInteractions (animationCallback );
167167 }
168+
169+ /**
170+ * Creates a following graph of nodes:
171+ * Value(1, firstValue) ----> Add(3) ---> Style(4) ---> Props(5) ---> View(viewTag)
172+ * |
173+ * Value(2, secondValue) --+
174+ *
175+ * Add(3) node maps to a "translateX" attribute of the Style(4) node.
176+ */
177+ private void createAnimatedGraphWithAdditionNode (
178+ int viewTag ,
179+ double firstValue ,
180+ double secondValue ) {
181+ mNativeAnimatedNodesManager .createAnimatedNode (
182+ 1 ,
183+ JavaOnlyMap .of ("type" , "value" , "value" , 100d ));
184+ mNativeAnimatedNodesManager .createAnimatedNode (
185+ 2 ,
186+ JavaOnlyMap .of ("type" , "value" , "value" , 1000d ));
187+
188+ mNativeAnimatedNodesManager .createAnimatedNode (
189+ 3 ,
190+ JavaOnlyMap .of ("type" , "addition" , "input" , JavaOnlyArray .of (1 , 2 )));
191+
192+ mNativeAnimatedNodesManager .createAnimatedNode (
193+ 4 ,
194+ JavaOnlyMap .of ("type" , "style" , "style" , JavaOnlyMap .of ("translateX" , 3 )));
195+ mNativeAnimatedNodesManager .createAnimatedNode (
196+ 5 ,
197+ JavaOnlyMap .of ("type" , "props" , "props" , JavaOnlyMap .of ("style" , 4 )));
198+ mNativeAnimatedNodesManager .connectAnimatedNodes (1 , 3 );
199+ mNativeAnimatedNodesManager .connectAnimatedNodes (2 , 3 );
200+ mNativeAnimatedNodesManager .connectAnimatedNodes (3 , 4 );
201+ mNativeAnimatedNodesManager .connectAnimatedNodes (4 , 5 );
202+ mNativeAnimatedNodesManager .connectAnimatedNodeToView (5 , 50 );
203+ }
204+
205+ @ Test
206+ public void testAdditionNode () {
207+ createAnimatedGraphWithAdditionNode (50 , 100d , 1000d );
208+
209+ Callback animationCallback = mock (Callback .class );
210+ JavaOnlyArray frames = JavaOnlyArray .of (0d , 1d );
211+ mNativeAnimatedNodesManager .startAnimatingNode (
212+ 1 ,
213+ JavaOnlyMap .of ("type" , "frames" , "frames" , frames , "toValue" , 101d ),
214+ animationCallback );
215+
216+ mNativeAnimatedNodesManager .startAnimatingNode (
217+ 2 ,
218+ JavaOnlyMap .of ("type" , "frames" , "frames" , frames , "toValue" , 1010d ),
219+ animationCallback );
220+
221+ ArgumentCaptor <ReactStylesDiffMap > stylesCaptor =
222+ ArgumentCaptor .forClass (ReactStylesDiffMap .class );
223+
224+ reset (mUIImplementationMock );
225+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
226+ verify (mUIImplementationMock ).synchronouslyUpdateViewOnUIThread (eq (50 ), stylesCaptor .capture ());
227+ assertThat (stylesCaptor .getValue ().getDouble ("translateX" , Double .NaN )).isEqualTo (1100d );
228+
229+ reset (mUIImplementationMock );
230+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
231+ verify (mUIImplementationMock )
232+ .synchronouslyUpdateViewOnUIThread (eq (50 ), stylesCaptor .capture ());
233+ assertThat (stylesCaptor .getValue ().getDouble ("translateX" , Double .NaN )).isEqualTo (1100d );
234+
235+ reset (mUIImplementationMock );
236+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
237+ verify (mUIImplementationMock )
238+ .synchronouslyUpdateViewOnUIThread (eq (50 ), stylesCaptor .capture ());
239+ assertThat (stylesCaptor .getValue ().getDouble ("translateX" , Double .NaN )).isEqualTo (1111d );
240+
241+ reset (mUIImplementationMock );
242+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
243+ verifyNoMoreInteractions (mUIImplementationMock );
244+ }
245+
246+ /**
247+ * Verifies that {@link NativeAnimatedNodesManager#runUpdates} updates the view correctly in case
248+ * when one of the addition input nodes has started animating while the other one has not.
249+ *
250+ * We expect that the output of the addition node will take the starting value of the second input
251+ * node even though the node hasn't been connected to an active animation driver.
252+ */
253+ @ Test
254+ public void testViewReceiveUpdatesIfOneOfAnimationHasntStarted () {
255+ createAnimatedGraphWithAdditionNode (50 , 100d , 1000d );
256+
257+ // Start animating only the first addition input node
258+ Callback animationCallback = mock (Callback .class );
259+ JavaOnlyArray frames = JavaOnlyArray .of (0d , 1d );
260+ mNativeAnimatedNodesManager .startAnimatingNode (
261+ 1 ,
262+ JavaOnlyMap .of ("type" , "frames" , "frames" , frames , "toValue" , 101d ),
263+ animationCallback );
264+
265+ ArgumentCaptor <ReactStylesDiffMap > stylesCaptor =
266+ ArgumentCaptor .forClass (ReactStylesDiffMap .class );
267+
268+ reset (mUIImplementationMock );
269+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
270+ verify (mUIImplementationMock ).synchronouslyUpdateViewOnUIThread (eq (50 ), stylesCaptor .capture ());
271+ assertThat (stylesCaptor .getValue ().getDouble ("translateX" , Double .NaN )).isEqualTo (1100d );
272+
273+ reset (mUIImplementationMock );
274+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
275+ verify (mUIImplementationMock )
276+ .synchronouslyUpdateViewOnUIThread (eq (50 ), stylesCaptor .capture ());
277+ assertThat (stylesCaptor .getValue ().getDouble ("translateX" , Double .NaN )).isEqualTo (1100d );
278+
279+ reset (mUIImplementationMock );
280+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
281+ verify (mUIImplementationMock )
282+ .synchronouslyUpdateViewOnUIThread (eq (50 ), stylesCaptor .capture ());
283+ assertThat (stylesCaptor .getValue ().getDouble ("translateX" , Double .NaN )).isEqualTo (1101d );
284+
285+ reset (mUIImplementationMock );
286+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
287+ verifyNoMoreInteractions (mUIImplementationMock );
288+ }
289+
290+ /**
291+ * Verifies that {@link NativeAnimatedNodesManager#runUpdates} updates the view correctly in case
292+ * when one of the addition input nodes animation finishes before the other.
293+ *
294+ * We expect that the output of the addition node after one of the animation has finished will
295+ * take the last value of the animated node and the view will receive updates up until the second
296+ * animation is over.
297+ */
298+ @ Test
299+ public void testViewReceiveUpdatesWhenOneOfAnimationHasFinished () {
300+ createAnimatedGraphWithAdditionNode (50 , 100d , 1000d );
301+
302+ Callback animationCallback = mock (Callback .class );
303+
304+ // Start animating for the first addition input node, will have 2 frames only
305+ JavaOnlyArray firstFrames = JavaOnlyArray .of (0d , 1d );
306+ mNativeAnimatedNodesManager .startAnimatingNode (
307+ 1 ,
308+ JavaOnlyMap .of ("type" , "frames" , "frames" , firstFrames , "toValue" , 200d ),
309+ animationCallback );
310+
311+ // Start animating for the first addition input node, will have 6 frames
312+ JavaOnlyArray secondFrames = JavaOnlyArray .of (0d , 0.2d , 0.4d , 0.6d , 0.8d , 1d );
313+ mNativeAnimatedNodesManager .startAnimatingNode (
314+ 2 ,
315+ JavaOnlyMap .of ("type" , "frames" , "frames" , secondFrames , "toValue" , 1010d ),
316+ animationCallback );
317+
318+ ArgumentCaptor <ReactStylesDiffMap > stylesCaptor =
319+ ArgumentCaptor .forClass (ReactStylesDiffMap .class );
320+
321+ reset (mUIImplementationMock );
322+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
323+ verify (mUIImplementationMock ).synchronouslyUpdateViewOnUIThread (eq (50 ), stylesCaptor .capture ());
324+ assertThat (stylesCaptor .getValue ().getDouble ("translateX" , Double .NaN )).isEqualTo (1100d );
325+
326+ reset (mUIImplementationMock );
327+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
328+ verify (mUIImplementationMock ).synchronouslyUpdateViewOnUIThread (eq (50 ), stylesCaptor .capture ());
329+ assertThat (stylesCaptor .getValue ().getDouble ("translateX" , Double .NaN )).isEqualTo (1100d );
330+
331+ for (int i = 1 ; i < secondFrames .size (); i ++) {
332+ reset (mUIImplementationMock );
333+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
334+ verify (mUIImplementationMock )
335+ .synchronouslyUpdateViewOnUIThread (eq (50 ), stylesCaptor .capture ());
336+ assertThat (stylesCaptor .getValue ().getDouble ("translateX" , Double .NaN ))
337+ .isEqualTo (1200d + secondFrames .getDouble (i ) * 10d );
338+ }
339+
340+ reset (mUIImplementationMock );
341+ mNativeAnimatedNodesManager .runUpdates (nextFrameTime ());
342+ verifyNoMoreInteractions (mUIImplementationMock );
343+ }
168344}
0 commit comments