diffing.rs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. #![allow(unused, non_upper_case_globals)]
  2. //! Diffing Tests
  3. //!
  4. //! These tests only verify that the diffing algorithm works properly for single components.
  5. //!
  6. //! It does not validated that component lifecycles work properly. This is done in another test file.
  7. use dioxus::{prelude::*, DomEdit};
  8. use dioxus_core as dioxus;
  9. use dioxus_core_macro::*;
  10. use dioxus_html as dioxus_elements;
  11. mod test_logging;
  12. fn new_dom() -> VirtualDom {
  13. const IS_LOGGING_ENABLED: bool = false;
  14. test_logging::set_up_logging(IS_LOGGING_ENABLED);
  15. VirtualDom::new(|cx| rsx!(cx, "hi"))
  16. }
  17. use DomEdit::*;
  18. /// Should push the text node onto the stack and modify it
  19. #[test]
  20. fn html_and_rsx_generate_the_same_output() {
  21. let dom = new_dom();
  22. let (create, change) = dom.diff_lazynodes(
  23. rsx! ( div { "Hello world" } ),
  24. rsx! ( div { "Goodbye world" } ),
  25. );
  26. assert_eq!(
  27. create.edits,
  28. [
  29. CreateElement { root: 1, tag: "div" },
  30. CreateTextNode { root: 2, text: "Hello world" },
  31. AppendChildren { many: 1 },
  32. AppendChildren { many: 1 },
  33. ]
  34. );
  35. assert_eq!(change.edits, [SetText { text: "Goodbye world", root: 2 },]);
  36. }
  37. /// Should result in 3 elements on the stack
  38. #[test]
  39. fn fragments_create_properly() {
  40. let dom = new_dom();
  41. let create = dom.create_vnodes(rsx! {
  42. div { "Hello a" }
  43. div { "Hello b" }
  44. div { "Hello c" }
  45. });
  46. assert_eq!(
  47. create.edits,
  48. [
  49. CreateElement { root: 1, tag: "div" },
  50. CreateTextNode { root: 2, text: "Hello a" },
  51. AppendChildren { many: 1 },
  52. CreateElement { root: 3, tag: "div" },
  53. CreateTextNode { root: 4, text: "Hello b" },
  54. AppendChildren { many: 1 },
  55. CreateElement { root: 5, tag: "div" },
  56. CreateTextNode { root: 6, text: "Hello c" },
  57. AppendChildren { many: 1 },
  58. AppendChildren { many: 3 },
  59. ]
  60. );
  61. }
  62. /// Should result in the creation of an anchor (placeholder) and then a replacewith
  63. #[test]
  64. fn empty_fragments_create_anchors() {
  65. let dom = new_dom();
  66. let left = rsx!({ (0..0).map(|_f| rsx! { div {}}) });
  67. let right = rsx!({ (0..1).map(|_f| rsx! { div {}}) });
  68. let (create, change) = dom.diff_lazynodes(left, right);
  69. assert_eq!(
  70. create.edits,
  71. [CreatePlaceholder { root: 1 }, AppendChildren { many: 1 }]
  72. );
  73. assert_eq!(
  74. change.edits,
  75. [
  76. CreateElement { root: 2, tag: "div" },
  77. ReplaceWith { m: 1, root: 1 }
  78. ]
  79. );
  80. }
  81. /// Should result in the creation of an anchor (placeholder) and then a replacewith m=5
  82. #[test]
  83. fn empty_fragments_create_many_anchors() {
  84. let dom = new_dom();
  85. let left = rsx!({ (0..0).map(|_f| rsx! { div {}}) });
  86. let right = rsx!({ (0..5).map(|_f| rsx! { div {}}) });
  87. let (create, change) = dom.diff_lazynodes(left, right);
  88. assert_eq!(
  89. create.edits,
  90. [CreatePlaceholder { root: 1 }, AppendChildren { many: 1 }]
  91. );
  92. assert_eq!(
  93. change.edits,
  94. [
  95. CreateElement { root: 2, tag: "div" },
  96. CreateElement { root: 3, tag: "div" },
  97. CreateElement { root: 4, tag: "div" },
  98. CreateElement { root: 5, tag: "div" },
  99. CreateElement { root: 6, tag: "div" },
  100. ReplaceWith { m: 5, root: 1 }
  101. ]
  102. );
  103. }
  104. /// Should result in the creation of an anchor (placeholder) and then a replacewith
  105. /// Includes child nodes inside the fragment
  106. #[test]
  107. fn empty_fragments_create_anchors_with_many_children() {
  108. let dom = new_dom();
  109. let left = rsx!({ (0..0).map(|_| rsx! { div {} }) });
  110. let right = rsx!({
  111. (0..3).map(|f| {
  112. rsx! { div { "hello: {f}" }}
  113. })
  114. });
  115. let (create, change) = dom.diff_lazynodes(left, right);
  116. assert_eq!(
  117. create.edits,
  118. [CreatePlaceholder { root: 1 }, AppendChildren { many: 1 }]
  119. );
  120. assert_eq!(
  121. change.edits,
  122. [
  123. CreateElement { tag: "div", root: 2 },
  124. CreateTextNode { text: "hello: 0", root: 3 },
  125. AppendChildren { many: 1 },
  126. CreateElement { tag: "div", root: 4 },
  127. CreateTextNode { text: "hello: 1", root: 5 },
  128. AppendChildren { many: 1 },
  129. CreateElement { tag: "div", root: 6 },
  130. CreateTextNode { text: "hello: 2", root: 7 },
  131. AppendChildren { many: 1 },
  132. ReplaceWith { root: 1, m: 3 }
  133. ]
  134. );
  135. }
  136. /// Should result in every node being pushed and then replaced with an anchor
  137. #[test]
  138. fn many_items_become_fragment() {
  139. let dom = new_dom();
  140. let left = rsx!({
  141. (0..2).map(|_| {
  142. rsx! { div { "hello" }}
  143. })
  144. });
  145. let right = rsx!({ (0..0).map(|_| rsx! { div {} }) });
  146. let (create, change) = dom.diff_lazynodes(left, right);
  147. assert_eq!(
  148. create.edits,
  149. [
  150. CreateElement { root: 1, tag: "div" },
  151. CreateTextNode { text: "hello", root: 2 },
  152. AppendChildren { many: 1 },
  153. CreateElement { root: 3, tag: "div" },
  154. CreateTextNode { text: "hello", root: 4 },
  155. AppendChildren { many: 1 },
  156. AppendChildren { many: 2 },
  157. ]
  158. );
  159. assert_eq!(
  160. change.edits,
  161. [
  162. CreatePlaceholder { root: 5 },
  163. ReplaceWith { root: 1, m: 1 },
  164. Remove { root: 3 },
  165. ]
  166. );
  167. }
  168. /// Should result in no edits
  169. #[test]
  170. fn two_equal_fragments_are_equal() {
  171. let dom = new_dom();
  172. let left = rsx!({
  173. (0..2).map(|_| {
  174. rsx! { div { "hello" }}
  175. })
  176. });
  177. let right = rsx!({
  178. (0..2).map(|_| {
  179. rsx! { div { "hello" }}
  180. })
  181. });
  182. let (_create, change) = dom.diff_lazynodes(left, right);
  183. assert!(change.edits.is_empty());
  184. }
  185. /// Should result the creation of more nodes appended after the old last node
  186. #[test]
  187. fn two_fragments_with_differrent_elements_are_differet() {
  188. let dom = new_dom();
  189. let left = rsx!(
  190. { (0..2).map(|_| rsx! { div { }} ) }
  191. p {}
  192. );
  193. let right = rsx!(
  194. { (0..5).map(|_| rsx! (h1 { }) ) }
  195. p {}
  196. );
  197. let (_create, changes) = dom.diff_lazynodes(left, right);
  198. log::debug!("{:#?}", &changes);
  199. assert_eq!(
  200. changes.edits,
  201. [
  202. // create the new h1s
  203. CreateElement { tag: "h1", root: 4 },
  204. CreateElement { tag: "h1", root: 5 },
  205. CreateElement { tag: "h1", root: 6 },
  206. InsertAfter { root: 2, n: 3 },
  207. // replace the divs with new h1s
  208. CreateElement { tag: "h1", root: 7 },
  209. ReplaceWith { root: 1, m: 1 },
  210. CreateElement { tag: "h1", root: 8 },
  211. ReplaceWith { root: 2, m: 1 },
  212. ]
  213. );
  214. }
  215. /// Should result in multiple nodes destroyed - with changes to the first nodes
  216. #[test]
  217. fn two_fragments_with_differrent_elements_are_differet_shorter() {
  218. let dom = new_dom();
  219. let left = rsx!(
  220. {(0..5).map(|f| {rsx! { div { }}})}
  221. p {}
  222. );
  223. let right = rsx!(
  224. {(0..2).map(|f| {rsx! { h1 { }}})}
  225. p {}
  226. );
  227. let (create, change) = dom.diff_lazynodes(left, right);
  228. assert_eq!(
  229. create.edits,
  230. [
  231. CreateElement { root: 1, tag: "div" },
  232. CreateElement { root: 2, tag: "div" },
  233. CreateElement { root: 3, tag: "div" },
  234. CreateElement { root: 4, tag: "div" },
  235. CreateElement { root: 5, tag: "div" },
  236. CreateElement { root: 6, tag: "p" },
  237. AppendChildren { many: 6 },
  238. ]
  239. );
  240. assert_eq!(
  241. change.edits,
  242. [
  243. Remove { root: 3 },
  244. Remove { root: 4 },
  245. Remove { root: 5 },
  246. CreateElement { root: 7, tag: "h1" },
  247. ReplaceWith { root: 1, m: 1 },
  248. CreateElement { root: 8, tag: "h1" },
  249. ReplaceWith { root: 2, m: 1 },
  250. ]
  251. );
  252. }
  253. /// Should result in multiple nodes destroyed - with no changes
  254. #[test]
  255. fn two_fragments_with_same_elements_are_differet() {
  256. let dom = new_dom();
  257. let left = rsx!(
  258. {(0..2).map(|f| {rsx! { div { }}})}
  259. p {}
  260. );
  261. let right = rsx!(
  262. {(0..5).map(|f| {rsx! { div { }}})}
  263. p {}
  264. );
  265. let (create, change) = dom.diff_lazynodes(left, right);
  266. assert_eq!(
  267. create.edits,
  268. [
  269. CreateElement { root: 1, tag: "div" },
  270. CreateElement { root: 2, tag: "div" },
  271. CreateElement { root: 3, tag: "p" },
  272. AppendChildren { many: 3 },
  273. ]
  274. );
  275. assert_eq!(
  276. change.edits,
  277. [
  278. CreateElement { root: 4, tag: "div" },
  279. CreateElement { root: 5, tag: "div" },
  280. CreateElement { root: 6, tag: "div" },
  281. InsertAfter { root: 2, n: 3 },
  282. ]
  283. );
  284. }
  285. /// should result in the removal of elements
  286. #[test]
  287. fn keyed_diffing_order() {
  288. let dom = new_dom();
  289. let left = rsx!(
  290. {(0..5).map(|f| {rsx! { div { key: "{f}" }}})}
  291. p {"e"}
  292. );
  293. let right = rsx!(
  294. {(0..2).map(|f| rsx! { div { key: "{f}" }})}
  295. p {"e"}
  296. );
  297. let (create, change) = dom.diff_lazynodes(left, right);
  298. assert_eq!(
  299. change.edits,
  300. [Remove { root: 3 }, Remove { root: 4 }, Remove { root: 5 },]
  301. );
  302. }
  303. /// Should result in moves, but not removals or additions
  304. #[test]
  305. fn keyed_diffing_out_of_order() {
  306. let dom = new_dom();
  307. let left = rsx!({
  308. [0, 1, 2, 3, /**/ 4, 5, 6, /**/ 7, 8, 9].iter().map(|f| {
  309. rsx! { div { key: "{f}" }}
  310. })
  311. });
  312. let right = rsx!({
  313. [0, 1, 2, 3, /**/ 6, 4, 5, /**/ 7, 8, 9].iter().map(|f| {
  314. rsx! { div { key: "{f}" }}
  315. })
  316. });
  317. let (_, changes) = dom.diff_lazynodes(left, right);
  318. log::debug!("{:?}", &changes);
  319. assert_eq!(
  320. changes.edits,
  321. [PushRoot { root: 7 }, InsertBefore { root: 5, n: 1 }]
  322. );
  323. }
  324. /// Should result in moves only
  325. #[test]
  326. fn keyed_diffing_out_of_order_adds() {
  327. let dom = new_dom();
  328. let left = rsx!({
  329. [/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
  330. rsx! { div { key: "{f}" }}
  331. })
  332. });
  333. let right = rsx!({
  334. [/**/ 8, 7, 4, 5, 6 /**/].iter().map(|f| {
  335. rsx! { div { key: "{f}" }}
  336. })
  337. });
  338. let (_, change) = dom.diff_lazynodes(left, right);
  339. assert_eq!(
  340. change.edits,
  341. [
  342. PushRoot { root: 5 },
  343. PushRoot { root: 4 },
  344. InsertBefore { n: 2, root: 1 }
  345. ]
  346. );
  347. }
  348. /// Should result in moves onl
  349. #[test]
  350. fn keyed_diffing_out_of_order_adds_2() {
  351. let dom = new_dom();
  352. let left = rsx!({
  353. [/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
  354. rsx! { div { key: "{f}" }}
  355. })
  356. });
  357. let right = rsx!({
  358. [/**/ 7, 8, 4, 5, 6 /**/].iter().map(|f| {
  359. rsx! { div { key: "{f}" }}
  360. })
  361. });
  362. let (_, change) = dom.diff_lazynodes(left, right);
  363. assert_eq!(
  364. change.edits,
  365. [
  366. PushRoot { root: 4 },
  367. PushRoot { root: 5 },
  368. InsertBefore { n: 2, root: 1 }
  369. ]
  370. );
  371. }
  372. /// Should result in moves onl
  373. #[test]
  374. fn keyed_diffing_out_of_order_adds_3() {
  375. let dom = new_dom();
  376. let left = rsx!({
  377. [/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
  378. rsx! { div { key: "{f}" }}
  379. })
  380. });
  381. let right = rsx!({
  382. [/**/ 4, 8, 7, 5, 6 /**/].iter().map(|f| {
  383. rsx! { div { key: "{f}" }}
  384. })
  385. });
  386. let (_, change) = dom.diff_lazynodes(left, right);
  387. assert_eq!(
  388. change.edits,
  389. [
  390. PushRoot { root: 5 },
  391. PushRoot { root: 4 },
  392. InsertBefore { n: 2, root: 2 }
  393. ]
  394. );
  395. }
  396. /// Should result in moves onl
  397. #[test]
  398. fn keyed_diffing_out_of_order_adds_4() {
  399. let dom = new_dom();
  400. let left = rsx!({
  401. [/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
  402. rsx! { div { key: "{f}" }}
  403. })
  404. });
  405. let right = rsx!({
  406. [/**/ 4, 5, 8, 7, 6 /**/].iter().map(|f| {
  407. rsx! { div { key: "{f}" }}
  408. })
  409. });
  410. let (_, change) = dom.diff_lazynodes(left, right);
  411. assert_eq!(
  412. change.edits,
  413. [
  414. PushRoot { root: 5 },
  415. PushRoot { root: 4 },
  416. InsertBefore { n: 2, root: 3 }
  417. ]
  418. );
  419. }
  420. /// Should result in moves onl
  421. #[test]
  422. fn keyed_diffing_out_of_order_adds_5() {
  423. let dom = new_dom();
  424. let left = rsx!({
  425. [/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
  426. rsx! { div { key: "{f}" }}
  427. })
  428. });
  429. let right = rsx!({
  430. [/**/ 4, 5, 6, 8, 7 /**/].iter().map(|f| {
  431. rsx! { div { key: "{f}" }}
  432. })
  433. });
  434. let (_, change) = dom.diff_lazynodes(left, right);
  435. assert_eq!(
  436. change.edits,
  437. [PushRoot { root: 5 }, InsertBefore { n: 1, root: 4 }]
  438. );
  439. }
  440. #[test]
  441. fn keyed_diffing_additions() {
  442. let dom = new_dom();
  443. let left = rsx!({
  444. [/**/ 4, 5, 6, 7, 8 /**/].iter().map(|f| {
  445. rsx! { div { key: "{f}" }}
  446. })
  447. });
  448. let right = rsx!({
  449. [/**/ 4, 5, 6, 7, 8, 9, 10 /**/].iter().map(|f| {
  450. rsx! { div { key: "{f}" }}
  451. })
  452. });
  453. let (_, change) = dom.diff_lazynodes(left, right);
  454. assert_eq!(
  455. change.edits,
  456. [
  457. CreateElement { root: 6, tag: "div" },
  458. CreateElement { root: 7, tag: "div" },
  459. InsertAfter { n: 2, root: 5 }
  460. ]
  461. );
  462. }
  463. #[test]
  464. fn keyed_diffing_additions_and_moves_on_ends() {
  465. let dom = new_dom();
  466. let left = rsx!({
  467. [/**/ 4, 5, 6, 7 /**/].iter().map(|f| {
  468. rsx! { div { key: "{f}" }}
  469. })
  470. });
  471. let right = rsx!({
  472. [/**/ 7, 4, 5, 6, 11, 12 /**/].iter().map(|f| {
  473. rsx! { div { key: "{f}" }}
  474. })
  475. });
  476. let (_, change) = dom.diff_lazynodes(left, right);
  477. log::debug!("{:?}", change);
  478. assert_eq!(
  479. change.edits,
  480. [
  481. // create 11, 12
  482. CreateElement { tag: "div", root: 5 },
  483. CreateElement { tag: "div", root: 6 },
  484. InsertAfter { root: 3, n: 2 },
  485. // move 7 to the front
  486. PushRoot { root: 4 },
  487. InsertBefore { root: 1, n: 1 }
  488. ]
  489. );
  490. }
  491. #[test]
  492. fn keyed_diffing_additions_and_moves_in_middle() {
  493. let dom = new_dom();
  494. let left = rsx!({
  495. [/**/ 4, 5, 6, 7 /**/].iter().map(|f| {
  496. rsx! { div { key: "{f}" }}
  497. })
  498. });
  499. let right = rsx!({
  500. [/**/ 7, 4, 13, 17, 5, 11, 12, 6 /**/].iter().map(|f| {
  501. rsx! { div { key: "{f}" }}
  502. })
  503. });
  504. // LIS: 4, 5, 6
  505. let (_, change) = dom.diff_lazynodes(left, right);
  506. log::debug!("{:#?}", change);
  507. assert_eq!(
  508. change.edits,
  509. [
  510. // create 13, 17
  511. CreateElement { tag: "div", root: 5 },
  512. CreateElement { tag: "div", root: 6 },
  513. InsertBefore { root: 2, n: 2 },
  514. // create 11, 12
  515. CreateElement { tag: "div", root: 7 },
  516. CreateElement { tag: "div", root: 8 },
  517. InsertBefore { root: 3, n: 2 },
  518. // move 7
  519. PushRoot { root: 4 },
  520. InsertBefore { root: 1, n: 1 }
  521. ]
  522. );
  523. }
  524. #[test]
  525. fn controlled_keyed_diffing_out_of_order() {
  526. let dom = new_dom();
  527. let left = rsx!({
  528. [4, 5, 6, 7].iter().map(|f| {
  529. rsx! { div { key: "{f}" }}
  530. })
  531. });
  532. let right = rsx!({
  533. [0, 5, 9, 6, 4].iter().map(|f| {
  534. rsx! { div { key: "{f}" }}
  535. })
  536. });
  537. // LIS: 5, 6
  538. let (_, changes) = dom.diff_lazynodes(left, right);
  539. log::debug!("{:#?}", &changes);
  540. assert_eq!(
  541. changes.edits,
  542. [
  543. // move 4 to after 6
  544. PushRoot { root: 1 },
  545. InsertAfter { n: 1, root: 3 },
  546. // remove 7
  547. // create 9 and insert before 6
  548. CreateElement { root: 5, tag: "div" },
  549. InsertBefore { n: 1, root: 3 },
  550. // create 0 and insert before 5
  551. CreateElement { root: 6, tag: "div" },
  552. InsertBefore { n: 1, root: 2 },
  553. ]
  554. );
  555. }
  556. #[test]
  557. fn controlled_keyed_diffing_out_of_order_max_test() {
  558. let dom = new_dom();
  559. let left = rsx!({
  560. [0, 1, 2, 3, 4].iter().map(|f| {
  561. rsx! { div { key: "{f}" }}
  562. })
  563. });
  564. let right = rsx!({
  565. [3, 0, 1, 10, 2].iter().map(|f| {
  566. rsx! { div { key: "{f}" }}
  567. })
  568. });
  569. let (_, changes) = dom.diff_lazynodes(left, right);
  570. log::debug!("{:#?}", &changes);
  571. assert_eq!(
  572. changes.edits,
  573. [
  574. CreateElement { root: 6, tag: "div" },
  575. InsertBefore { n: 1, root: 3 },
  576. PushRoot { root: 4 },
  577. InsertBefore { n: 1, root: 1 },
  578. ]
  579. );
  580. }
  581. // noticed some weird behavior in the desktop interpreter
  582. // just making sure it doesnt happen in the core implementation
  583. #[test]
  584. fn remove_list() {
  585. let dom = new_dom();
  586. let left = rsx!({
  587. (0..10).rev().take(5).map(|i| {
  588. rsx! { Fragment { key: "{i}", "{i}" }}
  589. })
  590. });
  591. let right = rsx!({
  592. (0..10).rev().take(2).map(|i| {
  593. rsx! { Fragment { key: "{i}", "{i}" }}
  594. })
  595. });
  596. let (create, changes) = dom.diff_lazynodes(left, right);
  597. // dbg!(create);
  598. // dbg!(changes);
  599. assert_eq!(
  600. changes.edits,
  601. [
  602. // remove 5, 4, 3
  603. Remove { root: 3 },
  604. Remove { root: 4 },
  605. Remove { root: 5 },
  606. ]
  607. );
  608. }
  609. // noticed some weird behavior in the desktop interpreter
  610. // just making sure it doesnt happen in the core implementation
  611. #[test]
  612. fn remove_list_nokeyed() {
  613. let dom = new_dom();
  614. let left = rsx!({
  615. (0..10).rev().take(5).map(|i| {
  616. rsx! { Fragment { "{i}" }}
  617. })
  618. });
  619. let right = rsx!({
  620. (0..10).rev().take(2).map(|i| {
  621. rsx! { Fragment { "{i}" }}
  622. })
  623. });
  624. let (create, changes) = dom.diff_lazynodes(left, right);
  625. assert_eq!(
  626. changes.edits,
  627. [
  628. // remove 5, 4, 3
  629. Remove { root: 3 },
  630. Remove { root: 4 },
  631. Remove { root: 5 },
  632. ]
  633. );
  634. }