testCTestBinPacker.cxx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. #include <cstddef> // IWYU pragma: keep
  2. #include <iostream>
  3. #include <map>
  4. #include <string>
  5. #include <vector>
  6. #include "cmCTestBinPacker.h"
  7. #include "cmCTestResourceAllocator.h"
  8. struct ExpectedPackResult
  9. {
  10. std::vector<int> SlotsNeeded;
  11. std::map<std::string, cmCTestResourceAllocator::Resource> Resources;
  12. bool ExpectedReturnValue;
  13. std::vector<cmCTestBinPackerAllocation> ExpectedRoundRobinAllocations;
  14. std::vector<cmCTestBinPackerAllocation> ExpectedBlockAllocations;
  15. };
  16. static const std::vector<ExpectedPackResult> expectedResults
  17. {
  18. /* clang-format off */
  19. {
  20. { 2, 2, 2, 2 },
  21. { { "0", { 4, 0 } }, { "1", { 4, 0 } }, { "2", { 4, 0 } },
  22. { "3", { 4, 0 } } },
  23. true,
  24. {
  25. { 0, 2, "0" },
  26. { 1, 2, "1" },
  27. { 2, 2, "2" },
  28. { 3, 2, "3" },
  29. },
  30. {
  31. { 0, 2, "0" },
  32. { 1, 2, "0" },
  33. { 2, 2, "1" },
  34. { 3, 2, "1" },
  35. },
  36. },
  37. {
  38. { 2, 3, 2 },
  39. { { "0", { 5, 0 } }, { "1", { 2, 0 } } },
  40. true,
  41. {
  42. { 0, 2, "0" },
  43. { 1, 3, "0" },
  44. { 2, 2, "1" },
  45. },
  46. {
  47. { 0, 2, "0" },
  48. { 1, 3, "0" },
  49. { 2, 2, "1" },
  50. },
  51. },
  52. {
  53. { 1, 2, 3 },
  54. { { "0", { 1, 0 } }, { "1", { 2, 0 } }, { "2", { 2, 0 } } },
  55. false,
  56. { },
  57. { },
  58. },
  59. {
  60. { 48, 21, 31, 10, 40 },
  61. { { "0", { 81, 0 } }, { "1", { 68, 0 } }, { "2", { 20, 0 } },
  62. { "3", { 13, 0 } } },
  63. true,
  64. {
  65. { 0, 48, "0" },
  66. { 1, 21, "1" },
  67. { 2, 31, "0" },
  68. { 3, 10, "2" },
  69. { 4, 40, "1" },
  70. },
  71. {
  72. { 0, 48, "0" },
  73. { 1, 21, "1" },
  74. { 2, 31, "0" },
  75. { 3, 10, "2" },
  76. { 4, 40, "1" },
  77. },
  78. },
  79. {
  80. { 30, 31, 39, 67 },
  81. { { "0", { 16, 0 } }, { "1", { 81, 0 } }, { "2", { 97, 0 } } },
  82. true,
  83. {
  84. { 0, 30, "2" },
  85. { 1, 31, "1" },
  86. { 2, 39, "1" },
  87. { 3, 67, "2" },
  88. },
  89. {
  90. { 0, 30, "2" },
  91. { 1, 31, "1" },
  92. { 2, 39, "1" },
  93. { 3, 67, "2" },
  94. },
  95. },
  96. {
  97. { 63, 47, 1, 9 },
  98. { { "0", { 18, 0 } }, { "1", { 29, 0 } }, { "2", { 9, 0 } },
  99. { "3", { 52, 0 } } },
  100. false,
  101. { },
  102. { },
  103. },
  104. {
  105. { 22, 29, 46, 85 },
  106. { { "0", { 65, 0 } }, { "1", { 85, 0 } }, { "2", { 65, 0 } },
  107. { "3", { 78, 0 } } },
  108. true,
  109. {
  110. { 0, 22, "2" },
  111. { 1, 29, "0" },
  112. { 2, 46, "3" },
  113. { 3, 85, "1" },
  114. },
  115. {
  116. { 0, 22, "0" },
  117. { 1, 29, "3" },
  118. { 2, 46, "3" },
  119. { 3, 85, "1" },
  120. },
  121. },
  122. {
  123. { 66, 11, 34, 21 },
  124. { { "0", { 24, 0 } }, { "1", { 57, 0 } }, { "2", { 61, 0 } },
  125. { "3", { 51, 0 } } },
  126. false,
  127. { },
  128. { },
  129. },
  130. {
  131. { 72, 65, 67, 45 },
  132. { { "0", { 29, 0 } }, { "1", { 77, 0 } }, { "2", { 98, 0 } },
  133. { "3", { 58, 0 } } },
  134. false,
  135. { },
  136. { },
  137. },
  138. /*
  139. * The following is a contrived attack on the bin-packing algorithm that
  140. * causes it to execute with n! complexity, where n is the number of
  141. * resources. This case is very unrepresentative of real-world usage, and
  142. * has been documented but disabled. The bin-packing problem is NP-hard, and
  143. * we may not be able to fix this case at all.
  144. */
  145. #if 0
  146. {
  147. { 1000, 999, 998, 997, 996, 995, 994, 993, 992, 991, 19 },
  148. { { "0", { 1000, 0 } }, { "1", { 1001, 0 } }, { "2", { 1002, 0 } },
  149. { "3", { 1003, 0 } }, { "4", { 1004, 0 } }, { "5", { 1005, 0 } },
  150. { "6", { 1006, 0 } }, { "7", { 1007, 0 } }, { "8", { 1008, 0 } },
  151. { "9", { 1009, 0 } } },
  152. false,
  153. { },
  154. { },
  155. },
  156. #endif
  157. /*
  158. * These cases are more representative of real-world usage (the resource
  159. * sizes are all the same.)
  160. */
  161. {
  162. { 1000, 999, 998, 997, 996, 995, 994, 993, 992, 991, 10 },
  163. { { "0", { 1000, 0 } }, { "1", { 1000, 0 } }, { "2", { 1000, 0 } },
  164. { "3", { 1000, 0 } }, { "4", { 1000, 0 } }, { "5", { 1000, 0 } },
  165. { "6", { 1000, 0 } }, { "7", { 1000, 0 } }, { "8", { 1000, 0 } },
  166. { "9", { 1000, 0 } } },
  167. false,
  168. { },
  169. { },
  170. },
  171. {
  172. { 1000, 999, 998, 997, 996, 995, 994, 993, 992, 991, 9 },
  173. { { "0", { 1000, 0 } }, { "1", { 1000, 0 } }, { "2", { 1000, 0 } },
  174. { "3", { 1000, 0 } }, { "4", { 1000, 0 } }, { "5", { 1000, 0 } },
  175. { "6", { 1000, 0 } }, { "7", { 1000, 0 } }, { "8", { 1000, 0 } },
  176. { "9", { 1000, 0 } } },
  177. true,
  178. {
  179. { 0, 1000, "0" },
  180. { 1, 999, "1" },
  181. { 2, 998, "2" },
  182. { 3, 997, "3" },
  183. { 4, 996, "4" },
  184. { 5, 995, "5" },
  185. { 6, 994, "6" },
  186. { 7, 993, "7" },
  187. { 8, 992, "8" },
  188. { 9, 991, "9" },
  189. { 10, 9, "9" },
  190. },
  191. {
  192. { 0, 1000, "0" },
  193. { 1, 999, "1" },
  194. { 2, 998, "2" },
  195. { 3, 997, "3" },
  196. { 4, 996, "4" },
  197. { 5, 995, "5" },
  198. { 6, 994, "6" },
  199. { 7, 993, "7" },
  200. { 8, 992, "8" },
  201. { 9, 991, "9" },
  202. { 10, 9, "9" },
  203. },
  204. },
  205. /* clang-format on */
  206. };
  207. struct AllocationComparison
  208. {
  209. cmCTestBinPackerAllocation First;
  210. cmCTestBinPackerAllocation Second;
  211. bool Equal;
  212. };
  213. static const std::vector<AllocationComparison> comparisons{
  214. /* clang-format off */
  215. { { 0, 1, "0" }, { 0, 1, "0" }, true },
  216. { { 0, 1, "0" }, { 1, 1, "0" }, false },
  217. { { 0, 1, "0" }, { 0, 2, "0" }, false },
  218. { { 0, 1, "0" }, { 0, 1, "1" }, false },
  219. /* clang-format on */
  220. };
  221. static bool TestExpectedPackResult(const ExpectedPackResult& expected)
  222. {
  223. std::vector<cmCTestBinPackerAllocation> roundRobinAllocations;
  224. roundRobinAllocations.reserve(expected.SlotsNeeded.size());
  225. std::size_t index = 0;
  226. for (auto const& n : expected.SlotsNeeded) {
  227. roundRobinAllocations.push_back({ index++, n, "" });
  228. }
  229. bool roundRobinResult = cmAllocateCTestResourcesRoundRobin(
  230. expected.Resources, roundRobinAllocations);
  231. if (roundRobinResult != expected.ExpectedReturnValue) {
  232. std::cout
  233. << "cmAllocateCTestResourcesRoundRobin did not return expected value"
  234. << std::endl;
  235. return false;
  236. }
  237. if (roundRobinResult &&
  238. roundRobinAllocations != expected.ExpectedRoundRobinAllocations) {
  239. std::cout << "cmAllocateCTestResourcesRoundRobin did not return expected "
  240. "allocations"
  241. << std::endl;
  242. return false;
  243. }
  244. std::vector<cmCTestBinPackerAllocation> blockAllocations;
  245. blockAllocations.reserve(expected.SlotsNeeded.size());
  246. index = 0;
  247. for (auto const& n : expected.SlotsNeeded) {
  248. blockAllocations.push_back({ index++, n, "" });
  249. }
  250. bool blockResult =
  251. cmAllocateCTestResourcesBlock(expected.Resources, blockAllocations);
  252. if (blockResult != expected.ExpectedReturnValue) {
  253. std::cout << "cmAllocateCTestResourcesBlock did not return expected value"
  254. << std::endl;
  255. return false;
  256. }
  257. if (blockResult && blockAllocations != expected.ExpectedBlockAllocations) {
  258. std::cout << "cmAllocateCTestResourcesBlock did not return expected"
  259. " allocations"
  260. << std::endl;
  261. return false;
  262. }
  263. return true;
  264. }
  265. int testCTestBinPacker(int /*unused*/, char* /*unused*/[])
  266. {
  267. int retval = 0;
  268. for (auto const& comparison : comparisons) {
  269. if ((comparison.First == comparison.Second) != comparison.Equal) {
  270. std::cout << "Comparison did not match expected" << std::endl;
  271. retval = 1;
  272. }
  273. if ((comparison.First != comparison.Second) == comparison.Equal) {
  274. std::cout << "Comparison did not match expected" << std::endl;
  275. retval = 1;
  276. }
  277. }
  278. for (auto const& expected : expectedResults) {
  279. if (!TestExpectedPackResult(expected)) {
  280. retval = 1;
  281. }
  282. }
  283. return retval;
  284. }