testCTestBinPacker.cxx 7.3 KB

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