sdk_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. // Copyright 2019 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package sdk
  15. import (
  16. "log"
  17. "os"
  18. "runtime"
  19. "testing"
  20. "android/soong/android"
  21. "github.com/google/blueprint/proptools"
  22. )
  23. // Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE.
  24. func TestMain(m *testing.M) {
  25. if runtime.GOOS != "linux" {
  26. // b/145598135 - Generating host snapshots for anything other than linux is not supported.
  27. log.Printf("Skipping as sdk snapshot generation is only supported on linux not %s", runtime.GOOS)
  28. os.Exit(0)
  29. }
  30. os.Exit(m.Run())
  31. }
  32. func TestDepNotInRequiredSdks(t *testing.T) {
  33. testSdkError(t, `module "myjavalib".*depends on "otherlib".*that isn't part of the required SDKs:.*`, `
  34. sdk {
  35. name: "mysdk",
  36. java_header_libs: ["sdkmember"],
  37. }
  38. sdk_snapshot {
  39. name: "mysdk@1",
  40. java_header_libs: ["sdkmember_mysdk_1"],
  41. }
  42. java_import {
  43. name: "sdkmember",
  44. prefer: false,
  45. host_supported: true,
  46. }
  47. java_import {
  48. name: "sdkmember_mysdk_1",
  49. sdk_member_name: "sdkmember",
  50. host_supported: true,
  51. }
  52. java_library {
  53. name: "myjavalib",
  54. srcs: ["Test.java"],
  55. libs: [
  56. "sdkmember",
  57. "otherlib",
  58. ],
  59. system_modules: "none",
  60. sdk_version: "none",
  61. compile_dex: true,
  62. host_supported: true,
  63. apex_available: ["myapex"],
  64. }
  65. // this lib is no in mysdk
  66. java_library {
  67. name: "otherlib",
  68. srcs: ["Test.java"],
  69. system_modules: "none",
  70. sdk_version: "none",
  71. compile_dex: true,
  72. host_supported: true,
  73. }
  74. apex {
  75. name: "myapex",
  76. java_libs: ["myjavalib"],
  77. uses_sdks: ["mysdk@1"],
  78. key: "myapex.key",
  79. certificate: ":myapex.cert",
  80. }
  81. `)
  82. }
  83. // Ensure that prebuilt modules have the same effective visibility as the source
  84. // modules.
  85. func TestSnapshotVisibility(t *testing.T) {
  86. packageBp := `
  87. package {
  88. default_visibility: ["//other/foo"],
  89. }
  90. sdk {
  91. name: "mysdk",
  92. visibility: [
  93. "//other/foo",
  94. // This short form will be replaced with //package:__subpackages__ in the
  95. // generated sdk_snapshot.
  96. ":__subpackages__",
  97. ],
  98. prebuilt_visibility: [
  99. "//prebuilts/mysdk",
  100. ],
  101. java_header_libs: [
  102. "myjavalib",
  103. "mypublicjavalib",
  104. "mydefaultedjavalib",
  105. "myprivatejavalib",
  106. ],
  107. }
  108. java_library {
  109. name: "myjavalib",
  110. // Uses package default visibility
  111. srcs: ["Test.java"],
  112. system_modules: "none",
  113. sdk_version: "none",
  114. }
  115. java_defaults {
  116. name: "java-defaults",
  117. visibility: ["//other/bar"],
  118. }
  119. java_library {
  120. name: "mypublicjavalib",
  121. defaults: ["java-defaults"],
  122. visibility: ["//visibility:public"],
  123. srcs: ["Test.java"],
  124. system_modules: "none",
  125. sdk_version: "none",
  126. }
  127. java_defaults {
  128. name: "myjavadefaults",
  129. visibility: ["//other/bar"],
  130. }
  131. java_library {
  132. name: "mydefaultedjavalib",
  133. defaults: ["myjavadefaults"],
  134. srcs: ["Test.java"],
  135. system_modules: "none",
  136. sdk_version: "none",
  137. }
  138. java_library {
  139. name: "myprivatejavalib",
  140. srcs: ["Test.java"],
  141. visibility: ["//visibility:private"],
  142. system_modules: "none",
  143. sdk_version: "none",
  144. }
  145. `
  146. result := testSdkWithFs(t, ``,
  147. map[string][]byte{
  148. "package/Test.java": nil,
  149. "package/Android.bp": []byte(packageBp),
  150. })
  151. CheckSnapshot(t, result, "mysdk", "package",
  152. checkAndroidBpContents(`
  153. // This is auto-generated. DO NOT EDIT.
  154. java_import {
  155. name: "mysdk_myjavalib@current",
  156. sdk_member_name: "myjavalib",
  157. visibility: [
  158. "//other/foo",
  159. "//package",
  160. "//prebuilts/mysdk",
  161. ],
  162. apex_available: ["//apex_available:platform"],
  163. jars: ["java/myjavalib.jar"],
  164. }
  165. java_import {
  166. name: "myjavalib",
  167. prefer: false,
  168. visibility: [
  169. "//other/foo",
  170. "//package",
  171. "//prebuilts/mysdk",
  172. ],
  173. apex_available: ["//apex_available:platform"],
  174. jars: ["java/myjavalib.jar"],
  175. }
  176. java_import {
  177. name: "mysdk_mypublicjavalib@current",
  178. sdk_member_name: "mypublicjavalib",
  179. visibility: ["//visibility:public"],
  180. apex_available: ["//apex_available:platform"],
  181. jars: ["java/mypublicjavalib.jar"],
  182. }
  183. java_import {
  184. name: "mypublicjavalib",
  185. prefer: false,
  186. visibility: ["//visibility:public"],
  187. apex_available: ["//apex_available:platform"],
  188. jars: ["java/mypublicjavalib.jar"],
  189. }
  190. java_import {
  191. name: "mysdk_mydefaultedjavalib@current",
  192. sdk_member_name: "mydefaultedjavalib",
  193. visibility: [
  194. "//other/bar",
  195. "//package",
  196. "//prebuilts/mysdk",
  197. ],
  198. apex_available: ["//apex_available:platform"],
  199. jars: ["java/mydefaultedjavalib.jar"],
  200. }
  201. java_import {
  202. name: "mydefaultedjavalib",
  203. prefer: false,
  204. visibility: [
  205. "//other/bar",
  206. "//package",
  207. "//prebuilts/mysdk",
  208. ],
  209. apex_available: ["//apex_available:platform"],
  210. jars: ["java/mydefaultedjavalib.jar"],
  211. }
  212. java_import {
  213. name: "mysdk_myprivatejavalib@current",
  214. sdk_member_name: "myprivatejavalib",
  215. visibility: [
  216. "//package",
  217. "//prebuilts/mysdk",
  218. ],
  219. apex_available: ["//apex_available:platform"],
  220. jars: ["java/myprivatejavalib.jar"],
  221. }
  222. java_import {
  223. name: "myprivatejavalib",
  224. prefer: false,
  225. visibility: [
  226. "//package",
  227. "//prebuilts/mysdk",
  228. ],
  229. apex_available: ["//apex_available:platform"],
  230. jars: ["java/myprivatejavalib.jar"],
  231. }
  232. sdk_snapshot {
  233. name: "mysdk@current",
  234. visibility: [
  235. "//other/foo",
  236. "//package:__subpackages__",
  237. ],
  238. java_header_libs: [
  239. "mysdk_myjavalib@current",
  240. "mysdk_mypublicjavalib@current",
  241. "mysdk_mydefaultedjavalib@current",
  242. "mysdk_myprivatejavalib@current",
  243. ],
  244. }
  245. `))
  246. }
  247. func TestPrebuiltVisibilityProperty_IsValidated(t *testing.T) {
  248. testSdkError(t, `prebuilt_visibility: cannot mix "//visibility:private" with any other visibility rules`, `
  249. sdk {
  250. name: "mysdk",
  251. prebuilt_visibility: [
  252. "//foo",
  253. "//visibility:private",
  254. ],
  255. }
  256. `)
  257. }
  258. func TestPrebuiltVisibilityProperty_AddPrivate(t *testing.T) {
  259. testSdkError(t, `prebuilt_visibility: "//visibility:private" does not widen the visibility`, `
  260. sdk {
  261. name: "mysdk",
  262. prebuilt_visibility: [
  263. "//visibility:private",
  264. ],
  265. java_header_libs: [
  266. "myjavalib",
  267. ],
  268. }
  269. java_library {
  270. name: "myjavalib",
  271. // Uses package default visibility
  272. srcs: ["Test.java"],
  273. system_modules: "none",
  274. sdk_version: "none",
  275. }
  276. `)
  277. }
  278. func TestSdkInstall(t *testing.T) {
  279. sdk := `
  280. sdk {
  281. name: "mysdk",
  282. }
  283. `
  284. result := testSdkWithFs(t, sdk, nil)
  285. CheckSnapshot(t, result, "mysdk", "",
  286. checkAllOtherCopyRules(`.intermediates/mysdk/common_os/mysdk-current.zip -> mysdk-current.zip`))
  287. }
  288. type EmbeddedPropertiesStruct struct {
  289. S_Embedded_Common string `android:"arch_variant"`
  290. S_Embedded_Different string `android:"arch_variant"`
  291. }
  292. type testPropertiesStruct struct {
  293. name string
  294. private string
  295. Public_Kept string `sdk:"keep"`
  296. S_Common string
  297. S_Different string `android:"arch_variant"`
  298. A_Common []string
  299. A_Different []string `android:"arch_variant"`
  300. F_Common *bool
  301. F_Different *bool `android:"arch_variant"`
  302. EmbeddedPropertiesStruct
  303. }
  304. func (p *testPropertiesStruct) optimizableProperties() interface{} {
  305. return p
  306. }
  307. func (p *testPropertiesStruct) String() string {
  308. return p.name
  309. }
  310. var _ propertiesContainer = (*testPropertiesStruct)(nil)
  311. func TestCommonValueOptimization(t *testing.T) {
  312. common := &testPropertiesStruct{name: "common"}
  313. structs := []propertiesContainer{
  314. &testPropertiesStruct{
  315. name: "struct-0",
  316. private: "common",
  317. Public_Kept: "common",
  318. S_Common: "common",
  319. S_Different: "upper",
  320. A_Common: []string{"first", "second"},
  321. A_Different: []string{"alpha", "beta"},
  322. F_Common: proptools.BoolPtr(false),
  323. F_Different: proptools.BoolPtr(false),
  324. EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
  325. S_Embedded_Common: "embedded_common",
  326. S_Embedded_Different: "embedded_upper",
  327. },
  328. },
  329. &testPropertiesStruct{
  330. name: "struct-1",
  331. private: "common",
  332. Public_Kept: "common",
  333. S_Common: "common",
  334. S_Different: "lower",
  335. A_Common: []string{"first", "second"},
  336. A_Different: []string{"alpha", "delta"},
  337. F_Common: proptools.BoolPtr(false),
  338. F_Different: proptools.BoolPtr(true),
  339. EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
  340. S_Embedded_Common: "embedded_common",
  341. S_Embedded_Different: "embedded_lower",
  342. },
  343. },
  344. }
  345. extractor := newCommonValueExtractor(common)
  346. err := extractor.extractCommonProperties(common, structs)
  347. android.AssertDeepEquals(t, "unexpected error", nil, err)
  348. android.AssertDeepEquals(t, "common properties not correct",
  349. &testPropertiesStruct{
  350. name: "common",
  351. private: "",
  352. Public_Kept: "",
  353. S_Common: "common",
  354. S_Different: "",
  355. A_Common: []string{"first", "second"},
  356. A_Different: []string(nil),
  357. F_Common: proptools.BoolPtr(false),
  358. F_Different: nil,
  359. EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
  360. S_Embedded_Common: "embedded_common",
  361. S_Embedded_Different: "",
  362. },
  363. },
  364. common)
  365. android.AssertDeepEquals(t, "updated properties[0] not correct",
  366. &testPropertiesStruct{
  367. name: "struct-0",
  368. private: "common",
  369. Public_Kept: "common",
  370. S_Common: "",
  371. S_Different: "upper",
  372. A_Common: nil,
  373. A_Different: []string{"alpha", "beta"},
  374. F_Common: nil,
  375. F_Different: proptools.BoolPtr(false),
  376. EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
  377. S_Embedded_Common: "",
  378. S_Embedded_Different: "embedded_upper",
  379. },
  380. },
  381. structs[0])
  382. android.AssertDeepEquals(t, "updated properties[1] not correct",
  383. &testPropertiesStruct{
  384. name: "struct-1",
  385. private: "common",
  386. Public_Kept: "common",
  387. S_Common: "",
  388. S_Different: "lower",
  389. A_Common: nil,
  390. A_Different: []string{"alpha", "delta"},
  391. F_Common: nil,
  392. F_Different: proptools.BoolPtr(true),
  393. EmbeddedPropertiesStruct: EmbeddedPropertiesStruct{
  394. S_Embedded_Common: "",
  395. S_Embedded_Different: "embedded_lower",
  396. },
  397. },
  398. structs[1])
  399. }
  400. func TestCommonValueOptimization_InvalidArchSpecificVariants(t *testing.T) {
  401. common := &testPropertiesStruct{name: "common"}
  402. structs := []propertiesContainer{
  403. &testPropertiesStruct{
  404. name: "struct-0",
  405. S_Common: "should-be-but-is-not-common0",
  406. },
  407. &testPropertiesStruct{
  408. name: "struct-1",
  409. S_Common: "should-be-but-is-not-common1",
  410. },
  411. }
  412. extractor := newCommonValueExtractor(common)
  413. err := extractor.extractCommonProperties(common, structs)
  414. android.AssertErrorMessageEquals(t, "unexpected error", `field "S_Common" is not tagged as "arch_variant" but has arch specific properties:
  415. "struct-0" has value "should-be-but-is-not-common0"
  416. "struct-1" has value "should-be-but-is-not-common1"`, err)
  417. }
  418. // Ensure that sdk snapshot related environment variables work correctly.
  419. func TestSnapshot_EnvConfiguration(t *testing.T) {
  420. bp := `
  421. sdk {
  422. name: "mysdk",
  423. java_header_libs: ["myjavalib"],
  424. }
  425. java_library {
  426. name: "myjavalib",
  427. srcs: ["Test.java"],
  428. system_modules: "none",
  429. sdk_version: "none",
  430. compile_dex: true,
  431. host_supported: true,
  432. }
  433. `
  434. preparer := android.GroupFixturePreparers(
  435. prepareForSdkTestWithJava,
  436. android.FixtureWithRootAndroidBp(bp),
  437. )
  438. checkZipFile := func(t *testing.T, result *android.TestResult, expected string) {
  439. zipRule := result.ModuleForTests("mysdk", "common_os").Rule("SnapshotZipFiles")
  440. android.AssertStringEquals(t, "snapshot zip file", expected, zipRule.Output.String())
  441. }
  442. t.Run("no env variables", func(t *testing.T) {
  443. result := preparer.RunTest(t)
  444. checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
  445. CheckSnapshot(t, result, "mysdk", "",
  446. checkAndroidBpContents(`
  447. // This is auto-generated. DO NOT EDIT.
  448. java_import {
  449. name: "mysdk_myjavalib@current",
  450. sdk_member_name: "myjavalib",
  451. visibility: ["//visibility:public"],
  452. apex_available: ["//apex_available:platform"],
  453. jars: ["java/myjavalib.jar"],
  454. }
  455. java_import {
  456. name: "myjavalib",
  457. prefer: false,
  458. visibility: ["//visibility:public"],
  459. apex_available: ["//apex_available:platform"],
  460. jars: ["java/myjavalib.jar"],
  461. }
  462. sdk_snapshot {
  463. name: "mysdk@current",
  464. visibility: ["//visibility:public"],
  465. java_header_libs: ["mysdk_myjavalib@current"],
  466. }
  467. `),
  468. )
  469. })
  470. t.Run("SOONG_SDK_SNAPSHOT_PREFER=true", func(t *testing.T) {
  471. result := android.GroupFixturePreparers(
  472. preparer,
  473. android.FixtureMergeEnv(map[string]string{
  474. "SOONG_SDK_SNAPSHOT_PREFER": "true",
  475. }),
  476. ).RunTest(t)
  477. checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
  478. CheckSnapshot(t, result, "mysdk", "",
  479. checkAndroidBpContents(`
  480. // This is auto-generated. DO NOT EDIT.
  481. java_import {
  482. name: "mysdk_myjavalib@current",
  483. sdk_member_name: "myjavalib",
  484. visibility: ["//visibility:public"],
  485. apex_available: ["//apex_available:platform"],
  486. jars: ["java/myjavalib.jar"],
  487. }
  488. java_import {
  489. name: "myjavalib",
  490. prefer: true,
  491. visibility: ["//visibility:public"],
  492. apex_available: ["//apex_available:platform"],
  493. jars: ["java/myjavalib.jar"],
  494. }
  495. sdk_snapshot {
  496. name: "mysdk@current",
  497. visibility: ["//visibility:public"],
  498. java_header_libs: ["mysdk_myjavalib@current"],
  499. }
  500. `),
  501. )
  502. })
  503. t.Run("SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR=module:build_from_source", func(t *testing.T) {
  504. result := android.GroupFixturePreparers(
  505. preparer,
  506. android.FixtureMergeEnv(map[string]string{
  507. "SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR": "module:build_from_source",
  508. }),
  509. ).RunTest(t)
  510. checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
  511. CheckSnapshot(t, result, "mysdk", "",
  512. checkAndroidBpContents(`
  513. // This is auto-generated. DO NOT EDIT.
  514. java_import {
  515. name: "mysdk_myjavalib@current",
  516. sdk_member_name: "myjavalib",
  517. visibility: ["//visibility:public"],
  518. apex_available: ["//apex_available:platform"],
  519. jars: ["java/myjavalib.jar"],
  520. }
  521. java_import {
  522. name: "myjavalib",
  523. prefer: false,
  524. use_source_config_var: {
  525. config_namespace: "module",
  526. var_name: "build_from_source",
  527. },
  528. visibility: ["//visibility:public"],
  529. apex_available: ["//apex_available:platform"],
  530. jars: ["java/myjavalib.jar"],
  531. }
  532. sdk_snapshot {
  533. name: "mysdk@current",
  534. visibility: ["//visibility:public"],
  535. java_header_libs: ["mysdk_myjavalib@current"],
  536. }
  537. `),
  538. )
  539. })
  540. t.Run("SOONG_SDK_SNAPSHOT_VERSION=unversioned", func(t *testing.T) {
  541. result := android.GroupFixturePreparers(
  542. preparer,
  543. android.FixtureMergeEnv(map[string]string{
  544. "SOONG_SDK_SNAPSHOT_VERSION": "unversioned",
  545. }),
  546. ).RunTest(t)
  547. checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk.zip")
  548. CheckSnapshot(t, result, "mysdk", "",
  549. checkAndroidBpContents(`
  550. // This is auto-generated. DO NOT EDIT.
  551. java_import {
  552. name: "myjavalib",
  553. prefer: false,
  554. visibility: ["//visibility:public"],
  555. apex_available: ["//apex_available:platform"],
  556. jars: ["java/myjavalib.jar"],
  557. }
  558. `),
  559. )
  560. })
  561. t.Run("SOONG_SDK_SNAPSHOT_VERSION=current", func(t *testing.T) {
  562. result := android.GroupFixturePreparers(
  563. preparer,
  564. android.FixtureMergeEnv(map[string]string{
  565. "SOONG_SDK_SNAPSHOT_VERSION": "current",
  566. }),
  567. ).RunTest(t)
  568. checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
  569. CheckSnapshot(t, result, "mysdk", "",
  570. checkAndroidBpContents(`
  571. // This is auto-generated. DO NOT EDIT.
  572. java_import {
  573. name: "mysdk_myjavalib@current",
  574. sdk_member_name: "myjavalib",
  575. visibility: ["//visibility:public"],
  576. apex_available: ["//apex_available:platform"],
  577. jars: ["java/myjavalib.jar"],
  578. }
  579. java_import {
  580. name: "myjavalib",
  581. prefer: false,
  582. visibility: ["//visibility:public"],
  583. apex_available: ["//apex_available:platform"],
  584. jars: ["java/myjavalib.jar"],
  585. }
  586. sdk_snapshot {
  587. name: "mysdk@current",
  588. visibility: ["//visibility:public"],
  589. java_header_libs: ["mysdk_myjavalib@current"],
  590. }
  591. `),
  592. )
  593. })
  594. t.Run("SOONG_SDK_SNAPSHOT_VERSION=2", func(t *testing.T) {
  595. result := android.GroupFixturePreparers(
  596. preparer,
  597. android.FixtureMergeEnv(map[string]string{
  598. "SOONG_SDK_SNAPSHOT_VERSION": "2",
  599. }),
  600. ).RunTest(t)
  601. checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-2.zip")
  602. CheckSnapshot(t, result, "mysdk", "",
  603. checkAndroidBpContents(`
  604. // This is auto-generated. DO NOT EDIT.
  605. java_import {
  606. name: "mysdk_myjavalib@2",
  607. sdk_member_name: "myjavalib",
  608. visibility: ["//visibility:public"],
  609. apex_available: ["//apex_available:platform"],
  610. jars: ["java/myjavalib.jar"],
  611. }
  612. sdk_snapshot {
  613. name: "mysdk@2",
  614. visibility: ["//visibility:public"],
  615. java_header_libs: ["mysdk_myjavalib@2"],
  616. }
  617. `),
  618. // A versioned snapshot cannot be used on its own so add the source back in.
  619. snapshotTestPreparer(checkSnapshotWithoutSource, android.FixtureWithRootAndroidBp(bp)),
  620. )
  621. })
  622. }