config_bp2build_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. // Copyright 2021 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 android
  15. import (
  16. "android/soong/bazel"
  17. "testing"
  18. )
  19. func TestExpandVars(t *testing.T) {
  20. android_arm64_config := TestConfig("out", nil, "", nil)
  21. android_arm64_config.BuildOS = Android
  22. android_arm64_config.BuildArch = Arm64
  23. testCases := []struct {
  24. description string
  25. config Config
  26. stringScope ExportedStringVariables
  27. stringListScope ExportedStringListVariables
  28. configVars ExportedConfigDependingVariables
  29. toExpand string
  30. expectedValues []string
  31. }{
  32. {
  33. description: "no expansion for non-interpolated value",
  34. toExpand: "foo",
  35. expectedValues: []string{"foo"},
  36. },
  37. {
  38. description: "single level expansion for string var",
  39. stringScope: ExportedStringVariables{
  40. "foo": "bar",
  41. },
  42. toExpand: "${foo}",
  43. expectedValues: []string{"bar"},
  44. },
  45. {
  46. description: "single level expansion with short-name for string var",
  47. stringScope: ExportedStringVariables{
  48. "foo": "bar",
  49. },
  50. toExpand: "${config.foo}",
  51. expectedValues: []string{"bar"},
  52. },
  53. {
  54. description: "single level expansion string list var",
  55. stringListScope: ExportedStringListVariables{
  56. "foo": []string{"bar"},
  57. },
  58. toExpand: "${foo}",
  59. expectedValues: []string{"bar"},
  60. },
  61. {
  62. description: "mixed level expansion for string list var",
  63. stringScope: ExportedStringVariables{
  64. "foo": "${bar}",
  65. "qux": "hello",
  66. },
  67. stringListScope: ExportedStringListVariables{
  68. "bar": []string{"baz", "${qux}"},
  69. },
  70. toExpand: "${foo}",
  71. expectedValues: []string{"baz hello"},
  72. },
  73. {
  74. description: "double level expansion",
  75. stringListScope: ExportedStringListVariables{
  76. "foo": []string{"${bar}"},
  77. "bar": []string{"baz"},
  78. },
  79. toExpand: "${foo}",
  80. expectedValues: []string{"baz"},
  81. },
  82. {
  83. description: "double level expansion with a literal",
  84. stringListScope: ExportedStringListVariables{
  85. "a": []string{"${b}", "c"},
  86. "b": []string{"d"},
  87. },
  88. toExpand: "${a}",
  89. expectedValues: []string{"d c"},
  90. },
  91. {
  92. description: "double level expansion, with two variables in a string",
  93. stringListScope: ExportedStringListVariables{
  94. "a": []string{"${b} ${c}"},
  95. "b": []string{"d"},
  96. "c": []string{"e"},
  97. },
  98. toExpand: "${a}",
  99. expectedValues: []string{"d e"},
  100. },
  101. {
  102. description: "triple level expansion with two variables in a string",
  103. stringListScope: ExportedStringListVariables{
  104. "a": []string{"${b} ${c}"},
  105. "b": []string{"${c}", "${d}"},
  106. "c": []string{"${d}"},
  107. "d": []string{"foo"},
  108. },
  109. toExpand: "${a}",
  110. expectedValues: []string{"foo foo foo"},
  111. },
  112. {
  113. description: "expansion with config depending vars",
  114. configVars: ExportedConfigDependingVariables{
  115. "a": func(c Config) string { return c.BuildOS.String() },
  116. "b": func(c Config) string { return c.BuildArch.String() },
  117. },
  118. config: android_arm64_config,
  119. toExpand: "${a}-${b}",
  120. expectedValues: []string{"android-arm64"},
  121. },
  122. {
  123. description: "double level multi type expansion",
  124. stringListScope: ExportedStringListVariables{
  125. "platform": []string{"${os}-${arch}"},
  126. "const": []string{"const"},
  127. },
  128. configVars: ExportedConfigDependingVariables{
  129. "os": func(c Config) string { return c.BuildOS.String() },
  130. "arch": func(c Config) string { return c.BuildArch.String() },
  131. "foo": func(c Config) string { return "foo" },
  132. },
  133. config: android_arm64_config,
  134. toExpand: "${const}/${platform}/${foo}",
  135. expectedValues: []string{"const/android-arm64/foo"},
  136. },
  137. }
  138. for _, testCase := range testCases {
  139. t.Run(testCase.description, func(t *testing.T) {
  140. output, _ := expandVar(testCase.config, testCase.toExpand, testCase.stringScope, testCase.stringListScope, testCase.configVars)
  141. if len(output) != len(testCase.expectedValues) {
  142. t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output))
  143. }
  144. for i, actual := range output {
  145. expectedValue := testCase.expectedValues[i]
  146. if actual != expectedValue {
  147. t.Errorf("Actual value '%s' doesn't match expected value '%s'", actual, expectedValue)
  148. }
  149. }
  150. })
  151. }
  152. }
  153. func TestBazelToolchainVars(t *testing.T) {
  154. testCases := []struct {
  155. name string
  156. config Config
  157. vars ExportedVariables
  158. expectedOut string
  159. }{
  160. {
  161. name: "exports strings",
  162. vars: ExportedVariables{
  163. exportedStringVars: ExportedStringVariables{
  164. "a": "b",
  165. "c": "d",
  166. },
  167. },
  168. expectedOut: bazel.GeneratedBazelFileWarning + `
  169. _a = "b"
  170. _c = "d"
  171. constants = struct(
  172. a = _a,
  173. c = _c,
  174. )`,
  175. },
  176. {
  177. name: "exports string lists",
  178. vars: ExportedVariables{
  179. exportedStringListVars: ExportedStringListVariables{
  180. "a": []string{"b1", "b2"},
  181. "c": []string{"d1", "d2"},
  182. },
  183. },
  184. expectedOut: bazel.GeneratedBazelFileWarning + `
  185. _a = [
  186. "b1",
  187. "b2",
  188. ]
  189. _c = [
  190. "d1",
  191. "d2",
  192. ]
  193. constants = struct(
  194. a = _a,
  195. c = _c,
  196. )`,
  197. },
  198. {
  199. name: "exports string lists dicts",
  200. vars: ExportedVariables{
  201. exportedStringListDictVars: ExportedStringListDictVariables{
  202. "a": map[string][]string{"b1": {"b2"}},
  203. "c": map[string][]string{"d1": {"d2"}},
  204. },
  205. },
  206. expectedOut: bazel.GeneratedBazelFileWarning + `
  207. _a = {
  208. "b1": ["b2"],
  209. }
  210. _c = {
  211. "d1": ["d2"],
  212. }
  213. constants = struct(
  214. a = _a,
  215. c = _c,
  216. )`,
  217. },
  218. {
  219. name: "exports dict with var refs",
  220. vars: ExportedVariables{
  221. exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{
  222. "a": map[string]string{"b1": "${b2}"},
  223. "c": map[string]string{"d1": "${config.d2}"},
  224. },
  225. },
  226. expectedOut: bazel.GeneratedBazelFileWarning + `
  227. _a = {
  228. "b1": _b2,
  229. }
  230. _c = {
  231. "d1": _d2,
  232. }
  233. constants = struct(
  234. a = _a,
  235. c = _c,
  236. )`,
  237. },
  238. {
  239. name: "sorts across types with variable references last",
  240. vars: ExportedVariables{
  241. exportedStringVars: ExportedStringVariables{
  242. "b": "b-val",
  243. "d": "d-val",
  244. },
  245. exportedStringListVars: ExportedStringListVariables{
  246. "c": []string{"c-val"},
  247. "e": []string{"e-val"},
  248. },
  249. exportedStringListDictVars: ExportedStringListDictVariables{
  250. "a": map[string][]string{"a1": {"a2"}},
  251. "f": map[string][]string{"f1": {"f2"}},
  252. },
  253. exportedVariableReferenceDictVars: ExportedVariableReferenceDictVariables{
  254. "aa": map[string]string{"b1": "${b}"},
  255. "cc": map[string]string{"d1": "${config.d}"},
  256. },
  257. },
  258. expectedOut: bazel.GeneratedBazelFileWarning + `
  259. _a = {
  260. "a1": ["a2"],
  261. }
  262. _b = "b-val"
  263. _c = ["c-val"]
  264. _d = "d-val"
  265. _e = ["e-val"]
  266. _f = {
  267. "f1": ["f2"],
  268. }
  269. _aa = {
  270. "b1": _b,
  271. }
  272. _cc = {
  273. "d1": _d,
  274. }
  275. constants = struct(
  276. a = _a,
  277. b = _b,
  278. c = _c,
  279. d = _d,
  280. e = _e,
  281. f = _f,
  282. aa = _aa,
  283. cc = _cc,
  284. )`,
  285. },
  286. }
  287. for _, tc := range testCases {
  288. t.Run(tc.name, func(t *testing.T) {
  289. out := BazelToolchainVars(tc.config, tc.vars)
  290. if out != tc.expectedOut {
  291. t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
  292. }
  293. })
  294. }
  295. }
  296. func TestSplitStringKeepingQuotedSubstring(t *testing.T) {
  297. testCases := []struct {
  298. description string
  299. s string
  300. delimiter byte
  301. split []string
  302. }{
  303. {
  304. description: "empty string returns single empty string",
  305. s: "",
  306. delimiter: ' ',
  307. split: []string{
  308. "",
  309. },
  310. },
  311. {
  312. description: "string with single space returns two empty strings",
  313. s: " ",
  314. delimiter: ' ',
  315. split: []string{
  316. "",
  317. "",
  318. },
  319. },
  320. {
  321. description: "string with two spaces returns three empty strings",
  322. s: " ",
  323. delimiter: ' ',
  324. split: []string{
  325. "",
  326. "",
  327. "",
  328. },
  329. },
  330. {
  331. description: "string with four words returns four word string",
  332. s: "hello world with words",
  333. delimiter: ' ',
  334. split: []string{
  335. "hello",
  336. "world",
  337. "with",
  338. "words",
  339. },
  340. },
  341. {
  342. description: "string with words and nested quote returns word strings and quote string",
  343. s: `hello "world with" words`,
  344. delimiter: ' ',
  345. split: []string{
  346. "hello",
  347. `"world with"`,
  348. "words",
  349. },
  350. },
  351. {
  352. description: "string with escaped quote inside real quotes",
  353. s: `hello \"world "with\" words"`,
  354. delimiter: ' ',
  355. split: []string{
  356. "hello",
  357. `"world`,
  358. `"with" words"`,
  359. },
  360. },
  361. {
  362. description: "string with words and escaped quotes returns word strings",
  363. s: `hello \"world with\" words`,
  364. delimiter: ' ',
  365. split: []string{
  366. "hello",
  367. `"world`,
  368. `with"`,
  369. "words",
  370. },
  371. },
  372. {
  373. description: "string which is single quoted substring returns only substring",
  374. s: `"hello world with words"`,
  375. delimiter: ' ',
  376. split: []string{
  377. `"hello world with words"`,
  378. },
  379. },
  380. {
  381. description: "string starting with quote returns quoted string",
  382. s: `"hello world with" words`,
  383. delimiter: ' ',
  384. split: []string{
  385. `"hello world with"`,
  386. "words",
  387. },
  388. },
  389. {
  390. description: "string with starting quote and no ending quote returns quote to end of string",
  391. s: `hello "world with words`,
  392. delimiter: ' ',
  393. split: []string{
  394. "hello",
  395. `"world with words`,
  396. },
  397. },
  398. {
  399. description: "quoted string is treated as a single \"word\" unless separated by delimiter",
  400. s: `hello "world"with words`,
  401. delimiter: ' ',
  402. split: []string{
  403. "hello",
  404. `"world"with`,
  405. "words",
  406. },
  407. },
  408. }
  409. for _, tc := range testCases {
  410. t.Run(tc.description, func(t *testing.T) {
  411. split := splitStringKeepingQuotedSubstring(tc.s, tc.delimiter)
  412. if len(split) != len(tc.split) {
  413. t.Fatalf("number of split string elements (%d) differs from expected (%d): split string (%v), expected (%v)",
  414. len(split), len(tc.split), split, tc.split,
  415. )
  416. }
  417. for i := range split {
  418. if split[i] != tc.split[i] {
  419. t.Errorf("split string element (%d), %v, differs from expected, %v", i, split[i], tc.split[i])
  420. }
  421. }
  422. })
  423. }
  424. }