assert.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <?php
  2. namespace Michel\UniTester;
  3. use Exception;
  4. use Michel\UniTester\Exception\AssertionFailureException;
  5. /**
  6. * Formats a value for display in assertion messages.
  7. *
  8. * @param mixed $value
  9. * @return string
  10. */
  11. function _dump($value): string
  12. {
  13. if (is_null($value)) {
  14. return 'null';
  15. }
  16. if (is_bool($value)) {
  17. return $value ? 'true' : 'false';
  18. }
  19. if (is_string($value)) {
  20. return "'{$value}'";
  21. }
  22. if (is_array($value)) {
  23. // Use json_encode for compact array representation
  24. return json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
  25. }
  26. if (is_object($value)) {
  27. return get_class($value) . ' ' . json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
  28. }
  29. if (is_resource($value)) {
  30. return get_resource_type($value);
  31. }
  32. return (string)$value;
  33. }
  34. /**
  35. * Formats a value with its type for display in assertion messages.
  36. *
  37. * @param mixed $value
  38. * @return string
  39. */
  40. function _dump_with_type($value): string
  41. {
  42. $type = gettype($value);
  43. $dump = _dump($value);
  44. return "{$dump} ({$type})";
  45. }
  46. function assert_strict_equals($expected, $actual, string $message = ''): void
  47. {
  48. if ($expected !== $actual) {
  49. $expectedStr = _dump_with_type($expected);
  50. $actualStr = _dump_with_type($actual);
  51. throw new AssertionFailureException($message ?: "Expected strictly {$expectedStr}, got {$actualStr}");
  52. }
  53. }
  54. function assert_equals($expected, $actual, string $message = ''): void
  55. {
  56. if ($expected != $actual) {
  57. $expectedStr = _dump($expected);
  58. $actualStr = _dump($actual);
  59. throw new AssertionFailureException($message ?: "Expected {$expectedStr}, got {$actualStr}");
  60. }
  61. }
  62. function assert_not_strict_equals($expected, $actual, string $message = ''): void
  63. {
  64. if ($expected === $actual) {
  65. $expectedStr = _dump_with_type($expected);
  66. throw new AssertionFailureException($message ?: "Expected value to not be strictly equal to {$expectedStr}");
  67. }
  68. }
  69. function assert_not_equals($expected, $actual, string $message = ''): void
  70. {
  71. if ($expected == $actual) {
  72. $expectedStr = _dump($expected);
  73. throw new AssertionFailureException($message ?: "Expected value to not be equal to {$expectedStr}");
  74. }
  75. }
  76. function assert_similar($expected, $actual, string $message = ''): void
  77. {
  78. if ($expected != $actual) {
  79. $expectedStr = _dump($expected);
  80. $actualStr = _dump($actual);
  81. throw new AssertionFailureException($message ?: "Expected similar to {$expectedStr}, got {$actualStr}");
  82. }
  83. }
  84. function assert_true($condition, string $message = ''): void
  85. {
  86. if ($condition !== true) {
  87. $actualStr = _dump_with_type($condition);
  88. throw new AssertionFailureException($message ?: "Expected true, got {$actualStr}");
  89. }
  90. }
  91. function assert_false($condition, string $message = ''): void
  92. {
  93. if ($condition !== false) {
  94. $actualStr = _dump_with_type($condition);
  95. throw new AssertionFailureException($message ?: "Expected false, got {$actualStr}");
  96. }
  97. }
  98. function assert_null($value, string $message = ''): void
  99. {
  100. if (!is_null($value)) {
  101. $actualStr = _dump_with_type($value);
  102. throw new AssertionFailureException($message ?: "Expected null, got {$actualStr}");
  103. }
  104. }
  105. function assert_not_null($value, string $message = ''): void
  106. {
  107. if (is_null($value)) {
  108. throw new AssertionFailureException($message ?: "Expected not null, got null");
  109. }
  110. }
  111. function assert_empty($value, string $message = ''): void
  112. {
  113. if (!empty($value)) {
  114. $actualStr = _dump_with_type($value);
  115. throw new AssertionFailureException($message ?: "Expected empty, got {$actualStr}");
  116. }
  117. }
  118. function assert_not_empty($value, string $message = ''): void
  119. {
  120. if (empty($value)) {
  121. $actualStr = _dump_with_type($value);
  122. throw new AssertionFailureException($message ?: "Expected not empty, got {$actualStr}");
  123. }
  124. }
  125. function assert_instanceof(string $expected, $actual, string $message = ''): void
  126. {
  127. if (!is_object($actual)) {
  128. $actualStr = _dump_with_type($actual);
  129. throw new AssertionFailureException($message ?: "Expected instance of {$expected}, got {$actualStr}");
  130. }
  131. if (!is_a($actual, $expected)) {
  132. $actualClass = get_class($actual);
  133. throw new AssertionFailureException($message ?: "Expected instance of {$expected}, got {$actualClass}");
  134. }
  135. }
  136. function assert_string_length($string, int $length, string $message = ''): void
  137. {
  138. if (!is_string($string)) {
  139. $actualStr = _dump_with_type($string);
  140. throw new AssertionFailureException($message ?: "Expected string, got {$actualStr}");
  141. }
  142. if (strlen($string) !== $length) {
  143. $actualLength = strlen($string);
  144. throw new AssertionFailureException($message ?: "Expected string length of {$length}, got {$actualLength}");
  145. }
  146. }
  147. function assert_string_contains($haystack, $needle, string $message = ''): void
  148. {
  149. if (!is_string($haystack)) {
  150. $actualStr = _dump_with_type($haystack);
  151. throw new AssertionFailureException($message ?: "Expected haystack to be string, got {$actualStr}");
  152. }
  153. if (!is_string($needle)) {
  154. $needleStr = _dump_with_type($needle);
  155. throw new AssertionFailureException($message ?: "Expected needle to be string, got {$needleStr}");
  156. }
  157. if (strpos($haystack, $needle) === false) {
  158. throw new AssertionFailureException($message ?: "Expected '{$haystack}' to contain '{$needle}'");
  159. }
  160. }
  161. function assert_string_starts_with($haystack, $needle, string $message = ''): void
  162. {
  163. if (!is_string($haystack)) {
  164. $actualStr = _dump_with_type($haystack);
  165. throw new AssertionFailureException($message ?: "Expected haystack to be string, got {$actualStr}");
  166. }
  167. if (!is_string($needle)) {
  168. $needleStr = _dump_with_type($needle);
  169. throw new AssertionFailureException($message ?: "Expected needle to be string, got {$needleStr}");
  170. }
  171. if (strpos($haystack, $needle) !== 0) {
  172. throw new AssertionFailureException($message ?: "Expected '{$haystack}' to start with '{$needle}'");
  173. }
  174. }
  175. function assert_string_ends_with($haystack, $needle, string $message = ''): void
  176. {
  177. if (!is_string($haystack)) {
  178. $actualStr = _dump_with_type($haystack);
  179. throw new AssertionFailureException($message ?: "Expected haystack to be string, got {$actualStr}");
  180. }
  181. if (!is_string($needle)) {
  182. $needleStr = _dump_with_type($needle);
  183. throw new AssertionFailureException($message ?: "Expected needle to be string, got {$needleStr}");
  184. }
  185. if (substr($haystack, -strlen($needle)) !== $needle) {
  186. throw new AssertionFailureException($message ?: "Expected '{$haystack}' to end with '{$needle}'");
  187. }
  188. }
  189. function assert_positive_int($value, string $message = ''): void
  190. {
  191. if (!is_int($value) || $value <= 0) {
  192. $actualStr = _dump_with_type($value);
  193. throw new AssertionFailureException($message ?: "Expected positive integer, got {$actualStr}");
  194. }
  195. }
  196. function assert_negative_int($value, string $message = ''): void
  197. {
  198. if (!is_int($value) || $value >= 0) {
  199. $actualStr = _dump_with_type($value);
  200. throw new AssertionFailureException($message ?: "Expected negative integer, got {$actualStr}");
  201. }
  202. }
  203. function assert_count(int $expectedCount, $haystack, string $message = ''): void
  204. {
  205. if (!is_array($haystack) && !$haystack instanceof \Countable) {
  206. $actualStr = _dump_with_type($haystack);
  207. throw new AssertionFailureException($message ?: "Expected array or Countable, got {$actualStr}");
  208. }
  209. $actualCount = count($haystack);
  210. if ($actualCount !== $expectedCount) {
  211. throw new AssertionFailureException($message ?: "Expected count {$expectedCount}, got {$actualCount}");
  212. }
  213. }
  214. function assert_array_has_key($key, $array, string $message = ''): void
  215. {
  216. if (!is_array($array) && !($array instanceof \ArrayAccess)) {
  217. $actualStr = _dump_with_type($array);
  218. throw new AssertionFailureException($message ?: "Expected array or ArrayAccess, got {$actualStr}");
  219. }
  220. if (is_array($array)) {
  221. if (!array_key_exists($key, $array)) {
  222. $keyStr = _dump($key);
  223. throw new AssertionFailureException($message ?: "Expected array to have key {$keyStr}");
  224. }
  225. } else {
  226. if (!$array->offsetExists($key)) {
  227. $keyStr = _dump($key);
  228. throw new AssertionFailureException($message ?: "Expected ArrayAccess to have key {$keyStr}");
  229. }
  230. }
  231. }
  232. function fail(string $message = ''): void
  233. {
  234. throw new AssertionFailureException($message ?: "Test failed");
  235. }
  236. function assert_execution_time_less_than(float $maxMs, callable $callback, string $message = ''): void
  237. {
  238. $start = microtime(true);
  239. $callback();
  240. $end = microtime(true);
  241. $executionTimeMs = ($end - $start) * 1000;
  242. if ($executionTimeMs > $maxMs) {
  243. throw new AssertionFailureException($message ?: "Expected execution time to be less than {$maxMs}ms, but it took {$executionTimeMs}ms");
  244. }
  245. }
  246. function assert_memory_usage_less_than(int $maxBytes, callable $callback, string $message = ''): void
  247. {
  248. $startMemory = memory_get_usage();
  249. $callback();
  250. $endMemory = memory_get_usage();
  251. $memoryUsage = $endMemory - $startMemory;
  252. if ($memoryUsage > $maxBytes) {
  253. throw new AssertionFailureException($message ?: "Expected memory usage to be less than {$maxBytes} bytes, but it used {$memoryUsage} bytes");
  254. }
  255. }
  256. function assert_file_exists(string $path, string $message = ''): void
  257. {
  258. if (!file_exists($path)) {
  259. throw new AssertionFailureException($message ?: "Expected file '{$path}' to exist");
  260. }
  261. }
  262. function assert_file_not_exists(string $path, string $message = ''): void
  263. {
  264. if (file_exists($path)) {
  265. throw new AssertionFailureException($message ?: "Expected file '{$path}' to not exist");
  266. }
  267. }
  268. function assert_directory_exists(string $path, string $message = ''): void
  269. {
  270. if (!is_dir($path)) {
  271. throw new AssertionFailureException($message ?: "Expected directory '{$path}' to exist");
  272. }
  273. }
  274. function assert_is_readable(string $path, string $message = ''): void
  275. {
  276. if (!is_readable($path)) {
  277. throw new AssertionFailureException($message ?: "Expected path '{$path}' to be readable");
  278. }
  279. }
  280. function assert_is_writable(string $path, string $message = ''): void
  281. {
  282. if (!is_writable($path)) {
  283. throw new AssertionFailureException($message ?: "Expected path '{$path}' to be writable");
  284. }
  285. }