1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798
|
;;; filenotify-tests.el --- Tests of file notifications -*- lexical-binding: t; -*-
;; Copyright (C) 2013-2025 Free Software Foundation, Inc.
;; Author: Michael Albinus <michael.albinus@gmx.de>
;; This file is part of GNU Emacs.
;;
;; GNU Emacs is free software: you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation, either version 3 of the
;; License, or (at your option) any later version.
;;
;; GNU Emacs is distributed in the hope that it will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Some of the tests require access to a remote host files. Since
;; this could be problematic, a mock-up connection method "mock" is
;; used. Emulating a remote connection, it simply calls "sh -i".
;; Tramp's file name handlers still run, so this test is sufficient
;; except for connection establishing.
;; If you want to test a real Tramp connection, set
;; $REMOTE_TEMPORARY_FILE_DIRECTORY to a suitable value in order to
;; overwrite the default value. If you want to skip tests accessing a
;; remote host, set this environment variable to "/dev/null" or
;; whatever is appropriate on your system.
;; For the remote file-notify library, Tramp checks for the existence
;; of a respective command. The first command found is used. In
;; order to use a dedicated one, the environment variable
;; $REMOTE_FILE_NOTIFY_LIBRARY shall be set, possible values are
;; "inotifywait", "gio-monitor" and "gvfs-monitor-dir".
;; Local file-notify libraries are auto-detected during Emacs
;; configuration. This can be changed with a respective configuration
;; argument, like
;;
;; --with-file-notification=inotify
;; --with-file-notification=kqueue
;; --with-file-notification=gfile
;; --with-file-notification=w32
;; A whole test run can be performed calling the command `file-notify-test-all'.
;;; Code:
(require 'tramp)
(require 'ert-x)
(require 'filenotify)
;; Filter suppressed remote file-notify libraries.
(when (stringp (getenv "REMOTE_FILE_NOTIFY_LIBRARY"))
(dolist (lib '("inotifywait" "gio-monitor" "gvfs-monitor-dir"))
(unless (string-equal (getenv "REMOTE_FILE_NOTIFY_LIBRARY") lib)
(add-to-list 'tramp-connection-properties `(nil ,lib nil)))))
(defvar file-notify--test-tmpdir nil)
(defvar file-notify--test-tmpfile nil)
(defvar file-notify--test-tmpfile1 nil)
(defvar file-notify--test-desc nil)
(defvar file-notify--test-desc1 nil)
(defvar file-notify--test-desc2 nil)
(defvar file-notify--test-results nil)
(defvar file-notify--test-event nil)
(defvar file-notify--test-file nil)
(defvar file-notify--test-events nil)
(defvar file-notify--test-monitors nil)
(defun file-notify--test-wait-event ()
"Wait for one event.
There are different timeouts for local and remote file notification libraries."
(read-event
nil nil
(cond
;; gio/gpollfilemonitor.c declares POLL_TIME_SECS 5. So we must
;; wait at least this time in the GPollFileMonitor case. A
;; similar timeout seems to be needed in the
;; GFam{File,Directory}Monitor case. So we use a large timeout
;; for any monitor.
((file-notify--test-monitor) 7)
((file-remote-p temporary-file-directory) 0.1)
(t 0.01)))
nil)
(defun file-notify--test-timeout ()
"Timeout to wait for arriving a bunch of events, in seconds."
(cond
((file-remote-p temporary-file-directory) 20)
((eq system-type 'cygwin) 10)
((getenv "EMACS_EMBA_CI") 10)
((string-equal (file-notify--test-library) "w32notify") 4)
(t 3)))
(defmacro file-notify--test-wait-for-events (timeout until)
"Wait for and return file notification events until form UNTIL is true.
TIMEOUT is the maximum time to wait for, in seconds."
`(with-timeout (,timeout (ignore))
(while (null ,until)
(file-notify--test-wait-event))))
(defun file-notify--test-no-descriptors ()
"Check that `file-notify-descriptors' is an empty hash table.
Return nil when any other file notification watch is still active."
;; Give read events a last chance.
(file-notify--test-wait-for-events
(file-notify--test-timeout)
(zerop (hash-table-count file-notify-descriptors)))
;; Now check.
(zerop (hash-table-count file-notify-descriptors)))
(defun file-notify--test-no-descriptors-explainer ()
"Explain why `file-notify--test-no-descriptors' fails."
(let ((result (list "Watch descriptor(s) existent:")))
(maphash
(lambda (key value) (push (cons key value) result))
file-notify-descriptors)
(nreverse result)))
(put 'file-notify--test-no-descriptors 'ert-explainer
'file-notify--test-no-descriptors-explainer)
(defun file-notify--test-cleanup-p ()
"Check, that the test has cleaned up the environment as much as needed."
;; `file-notify--test-event' should not be set but bound
;; dynamically.
(should-not file-notify--test-event)
;; The test should have cleaned up this already. Let's check
;; nevertheless.
(should (file-notify--test-no-descriptors)))
(defun file-notify--test-cleanup ()
"Cleanup after a test."
;; (when (getenv "EMACS_EMBA_CI")
;; (dolist (buf (tramp-list-tramp-buffers))
;; (message ";; %s\n%s" buf (tramp-get-buffer-string buf))
;; (kill-buffer buf)))
(file-notify-rm-all-watches)
(ignore-errors
(delete-file (file-newest-backup file-notify--test-tmpfile)))
(ignore-errors
(if (file-directory-p file-notify--test-tmpfile)
(delete-directory file-notify--test-tmpfile 'recursive)
(delete-file file-notify--test-tmpfile)))
(ignore-errors
(if (file-directory-p file-notify--test-tmpfile1)
(delete-directory file-notify--test-tmpfile1 'recursive)
(delete-file file-notify--test-tmpfile1)))
(ignore-errors
(delete-directory file-notify--test-tmpdir 'recursive))
(ignore-errors
(when (file-remote-p temporary-file-directory)
(tramp-cleanup-connection
(tramp-dissect-file-name temporary-file-directory) nil 'keep-password)))
(when (hash-table-p file-notify-descriptors)
(clrhash file-notify-descriptors))
(setq file-notify--test-tmpdir nil
file-notify--test-tmpfile nil
file-notify--test-tmpfile1 nil
file-notify--test-desc nil
file-notify--test-desc1 nil
file-notify--test-desc2 nil
file-notify--test-results nil
file-notify--test-event nil
file-notify--test-file nil
file-notify--test-events nil
file-notify--test-monitors nil))
(setq file-notify-debug nil
password-cache-expiry nil
;; tramp-verbose (if (getenv "EMACS_EMBA_CI") 10 0)
tramp-verbose 0
;; When the remote user id is 0, Tramp refuses unsafe temporary files.
tramp-allow-unsafe-temporary-files
(or tramp-allow-unsafe-temporary-files noninteractive))
(defun file-notify--test-add-watch (file flags callback)
"Like `file-notify-add-watch', but also passing FILE to CALLBACK."
(file-notify-add-watch file flags
(lambda (event) (funcall callback event file))))
;; We do not want to try and fail `file-notify-add-watch'.
(defun file-notify--test-local-enabled ()
"Whether local file notification is enabled.
This is needed for local `temporary-file-directory' only, in the
remote case we return always t."
(or file-notify--library
(file-remote-p temporary-file-directory)))
(defvar file-notify--test-remote-enabled-checked
(if (getenv "EMACS_HYDRA_CI") '(t . nil))
"Cached result of `file-notify--test-remote-enabled'.
If the function did run, the value is a cons cell, the `cdr'
being the result.")
(defun file-notify--test-remote-enabled ()
"Whether remote file notification is enabled."
(unless (consp file-notify--test-remote-enabled-checked)
(let (desc)
(ignore-errors
(and
(file-remote-p ert-remote-temporary-file-directory)
(file-directory-p ert-remote-temporary-file-directory)
(file-writable-p ert-remote-temporary-file-directory)
(setq desc
(file-notify-add-watch
ert-remote-temporary-file-directory
'(change) #'ignore))))
(setq file-notify--test-remote-enabled-checked (cons t desc))
(when desc (file-notify-rm-watch desc))))
;; Return result.
(cdr file-notify--test-remote-enabled-checked))
(defun file-notify--test-library ()
"The used library for the test, as a string.
In the remote case, it is the process name which runs on the
remote host, or nil."
(if (null (file-remote-p temporary-file-directory))
(symbol-name file-notify--library)
(and (consp file-notify--test-remote-enabled-checked)
(processp (cdr file-notify--test-remote-enabled-checked))
(replace-regexp-in-string
"<[[:digit:]]+>\\'" ""
(process-name (cdr file-notify--test-remote-enabled-checked))))))
(defun file-notify--test-monitor ()
"The used monitor for the test, as a symbol.
This returns only for (local) gfilenotify or (remote) gio library;
otherwise it is nil. `file-notify--test-desc' must be a valid
watch descriptor."
;; We cache the result, because after `file-notify-rm-watch',
;; `gfile-monitor-name' does not return a proper result anymore.
;; But we still need this information. So far, we know the monitors
;; GFamFileMonitor (gfilenotify on cygwin), GFamDirectoryMonitor
;; (gfilenotify on Solaris), GInotifyFileMonitor (gfilenotify and
;; gio on GNU/Linux), GKqueueFileMonitor (gfilenotify and gio on
;; FreeBSD) and GPollFileMonitor (gio on cygwin).
(when file-notify--test-desc
(or (alist-get file-notify--test-desc file-notify--test-monitors)
(when (member (file-notify--test-library) '("gfilenotify" "gio"))
(add-to-list
'file-notify--test-monitors
(cons file-notify--test-desc
(if (file-remote-p temporary-file-directory)
;; `file-notify--test-desc' is the connection process.
(progn
(while (not (tramp-connection-property-p
file-notify--test-desc "gio-file-monitor"))
(accept-process-output file-notify--test-desc 0))
(tramp-get-connection-property
file-notify--test-desc "gio-file-monitor" nil))
(and (functionp 'gfile-monitor-name)
(gfile-monitor-name file-notify--test-desc)))))
;; If we don't know the monitor, there are good chances the
;; test will fail. We let it fail already here, in order to
;; know the real reason.
(should (alist-get file-notify--test-desc file-notify--test-monitors)))
(alist-get file-notify--test-desc file-notify--test-monitors))))
(defmacro file-notify--deftest-remote (test docstring &optional unstable)
"Define ert `TEST-remote' for remote files.
If UNSTABLE is non-nil, the test is tagged as `:unstable'."
(declare (indent 1))
`(ert-deftest ,(intern (concat (symbol-name test) "-remote")) ()
,docstring
:tags (if ,unstable '(:expensive-test :unstable) '(:expensive-test))
(let* ((temporary-file-directory ert-remote-temporary-file-directory)
(ert-test (ert-get-test ',test))
vc-handled-backends)
(skip-unless (file-notify--test-remote-enabled))
;; These tests do not work for remote gio/GInotifyFileMonitor.
;; Needs further investigation.
(skip-when (string-equal (file-notify--test-library) "gio"))
(tramp-cleanup-connection
(tramp-dissect-file-name temporary-file-directory) nil 'keep-password)
(funcall (ert-test-body ert-test)))))
(ert-deftest file-notify-test00-availability ()
"Test availability of `file-notify'."
(skip-unless (file-notify--test-local-enabled))
(unwind-protect
(progn
;; Report the native library which has been used.
(message "Library: `%s'" (file-notify--test-library))
(should
(setq file-notify--test-desc
(file-notify-add-watch
temporary-file-directory '(change) #'ignore)))
(when (file-notify--test-monitor)
(message "Monitor: `%s'" (file-notify--test-monitor)))
(file-notify-rm-watch file-notify--test-desc)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test00-availability
"Test availability of `file-notify' for remote files.")
(defun file-notify--test-make-temp-name ()
"Create a temporary file name for test."
(unless (stringp file-notify--test-tmpdir)
(setq file-notify--test-tmpdir
(expand-file-name
(make-temp-name "file-notify-test") temporary-file-directory)))
(unless (file-directory-p file-notify--test-tmpdir)
(make-directory file-notify--test-tmpdir))
(expand-file-name
(make-temp-name "file-notify-test") file-notify--test-tmpdir))
(ert-deftest file-notify-test01-add-watch ()
"Check `file-notify-add-watch'."
(skip-unless (file-notify--test-local-enabled))
(unwind-protect
(progn
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
file-notify--test-tmpfile1
(format
"%s/%s" file-notify--test-tmpfile (md5 (current-time-string))))
;; Check, that different valid parameters are accepted.
(should
(setq file-notify--test-desc
(file-notify-add-watch
file-notify--test-tmpdir '(change) #'ignore)))
(file-notify-rm-watch file-notify--test-desc)
(should
(setq file-notify--test-desc
(file-notify-add-watch
file-notify--test-tmpdir '(attribute-change) #'ignore)))
(file-notify-rm-watch file-notify--test-desc)
(should
(setq file-notify--test-desc
(file-notify-add-watch
file-notify--test-tmpdir '(change attribute-change) #'ignore)))
(file-notify-rm-watch file-notify--test-desc)
;; File monitors like kqueue insist, that the watched file
;; exists. Directory monitors are not bound to this
;; restriction.
(when (string-equal (file-notify--test-library) "kqueue")
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message))
(should
(setq file-notify--test-desc
(file-notify-add-watch
file-notify--test-tmpfile '(change attribute-change) #'ignore)))
(file-notify-rm-watch file-notify--test-desc)
(when (string-equal (file-notify--test-library) "kqueue")
(delete-file file-notify--test-tmpfile))
;; Check error handling.
(should-error (file-notify-add-watch 1 2 3 4)
:type 'wrong-number-of-arguments)
(should
(equal (should-error
(file-notify-add-watch 1 2 3))
'(wrong-type-argument 1)))
(should
(equal (should-error
(file-notify-add-watch file-notify--test-tmpdir 2 3))
'(wrong-type-argument 2)))
(should
(equal (should-error
(file-notify-add-watch file-notify--test-tmpdir '(change) 3))
'(wrong-type-argument 3)))
;; The upper directory of a file must exist.
(should
(equal (should-error
(file-notify-add-watch
file-notify--test-tmpfile1
'(change attribute-change) #'ignore))
`(file-notify-error
"Directory does not exist" ,file-notify--test-tmpfile)))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test01-add-watch
"Check `file-notify-add-watch' for remote files.")
;; This test is inspired by Bug#26126 and Bug#26127.
(ert-deftest file-notify-test02-rm-watch ()
"Check `file-notify-rm-watch' and `file-notify-rm-all-watches'."
(skip-unless (file-notify--test-local-enabled))
(unwind-protect
;; Check, that `file-notify-rm-watch' works.
(progn
(should
(setq file-notify--test-desc
(file-notify-add-watch
temporary-file-directory '(change) #'ignore)))
(file-notify-rm-watch file-notify--test-desc)
;; Check, that any parameter is accepted.
(condition-case err
(progn
(file-notify-rm-watch nil)
(file-notify-rm-watch 0)
(file-notify-rm-watch "foo")
(file-notify-rm-watch 'foo))
(error (ert-fail err)))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
;; Check, that no error is returned removing a watch descriptor twice.
(progn
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
file-notify--test-tmpfile1 (file-notify--test-make-temp-name))
(write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
(write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message)
(should
(setq file-notify--test-desc
(file-notify-add-watch
file-notify--test-tmpfile '(change) #'ignore)))
(should
(setq file-notify--test-desc1
(file-notify-add-watch
file-notify--test-tmpfile1 '(change) #'ignore)))
;; Remove `file-notify--test-desc' twice.
(file-notify-rm-watch file-notify--test-desc)
(file-notify-rm-watch file-notify--test-desc)
(file-notify-rm-watch file-notify--test-desc1)
(delete-file file-notify--test-tmpfile)
(delete-file file-notify--test-tmpfile1)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
;; Check, that removing watch descriptors out of order do not
;; harm. This fails on cygwin because of timing issues unless a
;; long `sit-for' is added before the call to
;; `file-notify--test-wait-event'.
(unless (eq system-type 'cygwin)
(let (results)
(cl-flet ((first-callback (event)
(when (eq (file-notify--test-event-action event) 'deleted)
(push 1 results)))
(second-callback (event)
(when (eq (file-notify--test-event-action event) 'deleted)
(push 2 results))))
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message)
(should
(setq file-notify--test-desc
(file-notify-add-watch
file-notify--test-tmpfile
'(change) #'first-callback)))
(should
(setq file-notify--test-desc1
(file-notify-add-watch
file-notify--test-tmpfile
'(change) #'second-callback)))
;; `file-notify-rm-watch' confuses `file-notify--test-monitor'.
;; Initialize it in time.
(file-notify--test-monitor)
;; Remove first watch.
(file-notify-rm-watch file-notify--test-desc)
;; Only the second callback shall run.
(file-notify--test-wait-event)
(delete-file file-notify--test-tmpfile)
(file-notify--test-wait-for-events
(file-notify--test-timeout) results)
(should (equal results (list 2)))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
;; Check `file-notify-rm-all-watches'.
(progn
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
file-notify--test-tmpfile1 (file-notify--test-make-temp-name))
(write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
(write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message)
(should
(setq file-notify--test-desc
(file-notify-add-watch
file-notify--test-tmpfile '(change) #'ignore)))
(should
(setq file-notify--test-desc1
(file-notify-add-watch
file-notify--test-tmpfile1 '(change) #'ignore)))
(file-notify-rm-all-watches)
(delete-file file-notify--test-tmpfile)
(delete-file file-notify--test-tmpfile1)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test02-rm-watch
"Check `file-notify-rm-watch' for remote files.")
;; Accessors for the callback argument.
(defun file-notify--test-event-desc (event) (car event))
(defun file-notify--test-event-action (event) (nth 1 event))
(defun file-notify--test-event-file (event) (nth 2 event))
(defun file-notify--test-event-file1 (event) (nth 3 event))
(defun file-notify--test-event-test ()
"Ert test function to be called by `file-notify--test-event-handler'.
We cannot pass arguments, so we assume that `file-notify--test-event'
and `file-notify--test-file' are bound somewhere."
;; Check the descriptor.
(should (equal (file-notify--test-event-desc file-notify--test-event)
file-notify--test-desc))
;; Check the file name.
(should
(string-prefix-p
file-notify--test-file
(file-notify--test-event-file file-notify--test-event)))
;; Check the second file name if exists.
(when (eq (file-notify--test-event-action file-notify--test-event) 'renamed)
(should
(string-prefix-p
file-notify--test-file
(file-notify--test-event-file1 file-notify--test-event)))))
(defun file-notify--test-event-handler (event file)
"Run a test over FILE-NOTIFY--TEST-EVENT.
For later analysis, append the test result to `file-notify--test-results'
and the event to `file-notify--test-events'."
(let* ((file-notify--test-event event)
(file-notify--test-file file)
(result
(ert-run-test (make-ert-test :body 'file-notify--test-event-test))))
;; Do not add lock files, this would confuse the checks.
(unless (string-match
(regexp-quote ".#")
(file-notify--test-event-file file-notify--test-event))
(when file-notify-debug
(message "file-notify--test-event-handler result: %s event: %S"
(null (ert-test-failed-p result)) file-notify--test-event))
(setq file-notify--test-events
(append file-notify--test-events `(,file-notify--test-event))
file-notify--test-results
(append file-notify--test-results `(,result))))))
(defun file-notify--test-event-actions ()
"Helper function to return retrieved actions, as list."
(mapcar #'file-notify--test-event-action file-notify--test-events))
(defun file-notify--test-with-actions-check (actions)
"Check whether received actions match one of the ACTIONS alternatives."
(let (result)
(dolist (elt actions result)
(setq result
(or result
(if (eq (car elt) :random)
(equal (sort (cdr elt) 'string-lessp)
(sort (file-notify--test-event-actions)
'string-lessp))
(equal elt (file-notify--test-event-actions))))))
;; Do not report result in case we debug. Write messages instead.
(if file-notify-debug
(prog1 t
(if result
(message "Success\n%s" (file-notify--test-event-actions))
(message (file-notify--test-with-actions-explainer actions))))
result)))
(defun file-notify--test-with-actions-explainer (actions)
"Explain why `file-notify--test-with-actions-check' fails."
(if (null (cdr actions))
(format "Received actions do not match expected actions\n%s\n%s"
(file-notify--test-event-actions) (car actions))
(format
"Received actions do not match any sequence of expected actions\n%s\n%s"
(file-notify--test-event-actions) actions)))
(put 'file-notify--test-with-actions-check 'ert-explainer
'file-notify--test-with-actions-explainer)
(defmacro file-notify--test-with-actions (actions &rest body)
"Run BODY collecting actions and then compare with ACTIONS.
ACTIONS is either a simple list of actions, or a list of lists of
actions, which represent different possible results. The first
event of a list could be the pseudo event `:random', which is
just an indicator for comparison.
Don't wait longer than timeout seconds for the actions to be
delivered."
(declare (indent 1) (debug (form body)))
`(let* ((actions (if (consp (car ,actions)) ,actions (list ,actions)))
(max-length
(apply
'max
(mapcar
(lambda (x) (length (if (eq (car x) :random) (cdr x) x)))
actions)))
;; Don't stop while debugging.
(while-no-input-ignore-events
(cons 'file-notify while-no-input-ignore-events))
create-lockfiles)
;; Flush pending actions.
(file-notify--test-wait-event)
(file-notify--test-wait-for-events
(file-notify--test-timeout)
(not (input-pending-p)))
(setq file-notify--test-events nil
file-notify--test-results nil)
,@body
(file-notify--test-wait-for-events
;; More actions need more time. Use some fudge factor.
(* (ceiling max-length 100) (file-notify--test-timeout))
(= max-length (length file-notify--test-events)))
;; Check the result sequence just to make sure that all actions
;; are as expected.
(dolist (result file-notify--test-results)
(when (ert-test-failed-p result)
(ert-fail
(cadr (ert-test-result-with-condition-condition result)))))
;; One of the possible event sequences shall match.
(should (file-notify--test-with-actions-check actions))))
(ert-deftest file-notify-test03-events ()
"Check file creation/change/removal notifications."
:tags (if (getenv "EMACS_EMBA_CI")
'(:expensive-test :unstable)
'(:expensive-test))
(skip-unless (file-notify--test-local-enabled))
(unwind-protect
;; Check file creation, change and deletion. It doesn't work
;; for kqueue, because we don't use an implicit directory
;; monitor.
(unless (string-equal (file-notify--test-library) "kqueue")
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
(should
(setq file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpfile
'(change) #'file-notify--test-event-handler)))
(file-notify--test-with-actions
(cond
;; GFam{File,Directory}Monitor, GKqueueFileMonitor and
;; GPollFileMonitor do not report the `changed' event.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor
GKqueueFileMonitor GPollFileMonitor))
'(created deleted stopped))
(t '(created changed deleted stopped)))
(write-region
"another text" nil file-notify--test-tmpfile nil 'no-message)
(file-notify--test-wait-event)
(delete-file file-notify--test-tmpfile))
(file-notify-rm-watch file-notify--test-desc)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
(progn
;; Check file change and deletion.
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
(write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
(should
(setq file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpfile
'(change) #'file-notify--test-event-handler)))
(file-notify--test-with-actions
(cond
;; GFam{File,Directory}Monitor and GPollFileMonitor do
;; not detect the `changed' event reliably.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor GPollFileMonitor))
'((deleted stopped)
(changed deleted stopped)))
;; GKqueueFileMonitor does not report the `changed' event.
((eq (file-notify--test-monitor) 'GKqueueFileMonitor)
'(deleted stopped))
;; There could be one or two `changed' events.
(t '((changed deleted stopped)
(changed changed deleted stopped))))
(write-region
"another text" nil file-notify--test-tmpfile nil 'no-message)
(file-notify--test-wait-event)
(delete-file file-notify--test-tmpfile))
(file-notify-rm-watch file-notify--test-desc)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
;; Check file creation, change and deletion when watching a
;; directory. There must be a `stopped' event when deleting the
;; directory.
(let ((file-notify--test-tmpdir
(make-temp-file "file-notify-test-parent" t)))
(should
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpdir
'(change) #'file-notify--test-event-handler)))
(file-notify--test-with-actions
(cond
;; w32notify does not raise `deleted' and `stopped'
;; events for the watched directory.
((string-equal (file-notify--test-library) "w32notify")
'(created changed deleted))
;; On emba, `deleted' and `stopped' events of the
;; directory are not detected.
((getenv "EMACS_EMBA_CI")
'(created changed deleted))
;; There are two `deleted' events, for the file and for
;; the directory. Except for
;; GFam{File,Directory}Monitor, GPollFileMonitor and
;; kqueue. And GFam{File,Directory}Monitor and
;; GPollFileMonitor do not raise a `changed' event.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor GPollFileMonitor))
'(created deleted stopped))
((string-equal (file-notify--test-library) "kqueue")
'(created changed deleted stopped))
;; GKqueueFileMonitor does not report the `changed' event.
((eq (file-notify--test-monitor) 'GKqueueFileMonitor)
'(created deleted deleted stopped))
(t '(created changed deleted deleted stopped)))
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message)
(file-notify--test-wait-event)
(delete-directory file-notify--test-tmpdir 'recursive))
(file-notify-rm-watch file-notify--test-desc)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
;; Check copy of files inside a directory.
(let ((file-notify--test-tmpdir
(make-temp-file "file-notify-test-parent" t)))
(should
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpdir
'(change) #'file-notify--test-event-handler)))
(file-notify--test-with-actions
(cond
;; w32notify does not distinguish between `changed' and
;; `attribute-changed'. It does not raise `deleted' and
;; `stopped' events for the watched directory.
((string-equal (file-notify--test-library) "w32notify")
'(created changed created changed
changed changed changed
deleted deleted))
;; There are three `deleted' events, for two files and
;; for the directory. Except for
;; GFam{File,Directory}Monitor, GPollFileMonitor and
;; kqueue.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor GPollFileMonitor))
'(created created changed changed deleted stopped))
((string-equal (file-notify--test-library) "kqueue")
'(created changed created changed deleted stopped))
;; On emba, `deleted' and `stopped' events of the
;; directory are not detected.
((getenv "EMACS_EMBA_CI")
'(created changed created changed deleted deleted))
;; GKqueueFileMonitor does not report the `changed' event.
((eq (file-notify--test-monitor) 'GKqueueFileMonitor)
'(created created deleted deleted deleted stopped))
(t '(created changed created changed
deleted deleted deleted stopped)))
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message)
(file-notify--test-wait-event)
(copy-file file-notify--test-tmpfile file-notify--test-tmpfile1)
;; The next two events shall not be visible.
(file-notify--test-wait-event)
(set-file-modes file-notify--test-tmpfile 000 'nofollow)
(file-notify--test-wait-event)
(set-file-times file-notify--test-tmpfile '(0 0) 'nofollow)
(file-notify--test-wait-event)
(delete-directory file-notify--test-tmpdir 'recursive))
(file-notify-rm-watch file-notify--test-desc)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
;; Check rename of files inside a directory.
(let ((file-notify--test-tmpdir
(make-temp-file "file-notify-test-parent" t)))
(should
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpdir
'(change) #'file-notify--test-event-handler)))
(file-notify--test-with-actions
(cond
;; w32notify does not raise `deleted' and `stopped'
;; events for the watched directory.
((string-equal (file-notify--test-library) "w32notify")
'(created changed renamed deleted))
;; On emba, `deleted' and `stopped' events of the
;; directory are not detected.
((getenv "EMACS_EMBA_CI")
'(created changed renamed deleted))
;; There are two `deleted' events, for the file and for
;; the directory. Except for
;; GFam{File,Directory}Monitor, GPollfileMonitor and
;; kqueue. And GFam{File,Directory}Monitor and
;; GPollFileMonitor raise `created' and `deleted' events
;; instead of a `renamed' event.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor GPollFileMonitor))
'(created created deleted deleted stopped))
((string-equal (file-notify--test-library) "kqueue")
'(created changed renamed deleted stopped))
;; GKqueueFileMonitor does not report the `changed' event.
((eq (file-notify--test-monitor) 'GKqueueFileMonitor)
'(created renamed deleted deleted stopped))
(t '(created changed renamed deleted deleted stopped)))
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message)
(file-notify--test-wait-event)
(rename-file file-notify--test-tmpfile file-notify--test-tmpfile1)
;; After the rename, we won't get events anymore.
(file-notify--test-wait-event)
(delete-directory file-notify--test-tmpdir 'recursive))
(file-notify-rm-watch file-notify--test-desc)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
;; Check attribute change.
(progn
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message)
(should
(setq file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpfile
'(attribute-change) #'file-notify--test-event-handler)))
(file-notify--test-with-actions
(cond
;; w32notify does not distinguish between `changed' and
;; `attribute-changed'. Under MS Windows 7, we get four
;; `changed' events, and under MS Windows 10 just two.
;; Strange.
((string-equal (file-notify--test-library) "w32notify")
'((changed changed)
(changed changed changed changed)))
;; GFam{File,Directory}Monitor, GKqueueFileMonitor and
;; GPollFileMonitor do not report the `attribute-changed'
;; event.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor
GKqueueFileMonitor GPollFileMonitor))
'())
;; For GInotifyFileMonitor,`write-region' raises
;; also an `attribute-changed' event on gio.
((and (string-equal (file-notify--test-library) "gio")
(eq (file-notify--test-monitor) 'GInotifyFileMonitor))
'(attribute-changed attribute-changed attribute-changed))
;; For kqueue, `write-region' raises also an
;; `attribute-changed' event.
((string-equal (file-notify--test-library) "kqueue")
'(attribute-changed attribute-changed attribute-changed))
(t '(attribute-changed attribute-changed)))
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message)
(file-notify--test-wait-event)
(set-file-modes file-notify--test-tmpfile 000 'nofollow)
(file-notify--test-wait-event)
(set-file-times file-notify--test-tmpfile '(0 0) 'nofollow)
(file-notify--test-wait-event)
(delete-file file-notify--test-tmpfile))
(file-notify-rm-watch file-notify--test-desc)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test03-events
"Check file creation/change/removal notifications for remote files." t)
(require 'autorevert)
(setq auto-revert-notify-exclude-dir-regexp "nothing-to-be-excluded"
auto-revert-remote-files t
auto-revert-stop-on-user-input nil)
(ert-deftest file-notify-test04-autorevert ()
"Check autorevert via file notification."
:tags '(:expensive-test)
(skip-unless (file-notify--test-local-enabled))
;; Run with shortened `auto-revert-interval' for a faster test.
(let* ((auto-revert-interval 1)
(timeout (if (file-remote-p temporary-file-directory)
60 ; FIXME: can this be shortened?
(* auto-revert-interval 2.5)))
buf)
(auto-revert-set-timer)
(unwind-protect
(progn
;; In the remote case, `vc-refresh-state' returns undesired
;; error messages. Let's suppress them.
(advice-add 'vc-refresh-state :around 'ignore)
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message)
(setq buf (find-file-noselect file-notify--test-tmpfile))
(with-current-buffer buf
(should (string-equal (buffer-string) "any text"))
;; `buffer-stale--default-function' checks for
;; `verify-visited-file-modtime'. We must ensure that it
;; returns nil.
(sleep-for 1)
(auto-revert-mode 1)
(with-timeout (timeout (ignore))
(while (null auto-revert-notify-watch-descriptor)
(sleep-for 0.2)))
;; `file-notify--test-monitor' needs to know
;; `file-notify--test-desc' in order to compute proper
;; timeouts.
(setq file-notify--test-desc auto-revert-notify-watch-descriptor)
;; GKqueueFileMonitor does not report the `changed' event.
(skip-when (eq (file-notify--test-monitor) 'GKqueueFileMonitor))
;; Check, that file notification has been used.
(should auto-revert-mode)
(should auto-revert-use-notify)
(should auto-revert-notify-watch-descriptor)
;; Modify file. We wait for a second, in order to have
;; another timestamp.
(ert-with-message-capture captured-messages
(let ((inhibit-message t))
(sleep-for 1)
(write-region
"another text" nil file-notify--test-tmpfile nil 'no-message)
;; Check, that the buffer has been reverted.
(file-notify--test-wait-for-events
timeout
(string-match
(format-message "Reverting buffer `%s'." (buffer-name buf))
captured-messages))
(should (string-match "another text" (buffer-string)))))
;; Stop file notification. Autorevert shall still work via polling.
(file-notify-rm-watch auto-revert-notify-watch-descriptor)
(file-notify--test-wait-for-events
timeout (null auto-revert-notify-watch-descriptor))
(should auto-revert-use-notify)
(should-not
(file-notify-valid-p auto-revert-notify-watch-descriptor))
;; Modify file. We wait for two seconds, in order to
;; have another timestamp. One second seems to be too
;; short. And cygwin sporadically requires more than two.
(ert-with-message-capture captured-messages
(let ((inhibit-message t))
(sleep-for (if (eq system-type 'cygwin) 3 2))
(write-region
"foo bla" nil file-notify--test-tmpfile nil 'no-message)
;; Check, that the buffer has been reverted.
(file-notify--test-wait-for-events
timeout
(string-match
(format-message "Reverting buffer `%s'." (buffer-name buf))
captured-messages))
(should (string-match "foo bla" (buffer-string)))))
;; Stop autorevert, in order to cleanup descriptor.
(auto-revert-mode -1))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(advice-remove 'vc-refresh-state 'ignore)
(ignore-errors (kill-buffer buf))
(file-notify--test-cleanup))))
(file-notify--deftest-remote file-notify-test04-autorevert
"Check autorevert via file notification for remote files." t)
(ert-deftest file-notify-test05-file-validity ()
"Check `file-notify-valid-p' for files."
:tags '(:expensive-test)
(skip-unless (file-notify--test-local-enabled))
(unwind-protect
(progn
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
(write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
(should
(setq file-notify--test-desc
(file-notify-add-watch
file-notify--test-tmpfile '(change) #'ignore)))
(should (file-notify-valid-p file-notify--test-desc))
;; After calling `file-notify-rm-watch', the descriptor is not
;; valid anymore.
(file-notify-rm-watch file-notify--test-desc)
(should-not (file-notify-valid-p file-notify--test-desc))
(delete-file file-notify--test-tmpfile)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
(progn
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
(write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
(should
(setq file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpfile
'(change) #'file-notify--test-event-handler)))
(should (file-notify-valid-p file-notify--test-desc))
(file-notify--test-with-actions
(cond
;; GFam{File,Directory}Monitor do not
;; detect the `changed' event reliably.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor))
'((deleted stopped)
(changed deleted stopped)))
;; GKqueueFileMonitor does not report the `changed' event.
((eq (file-notify--test-monitor) 'GKqueueFileMonitor)
'(deleted stopped))
;; There could be one or two `changed' events.
(t '((changed deleted stopped)
(changed changed deleted stopped))))
(write-region
"another text" nil file-notify--test-tmpfile nil 'no-message)
(file-notify--test-wait-event)
(delete-file file-notify--test-tmpfile))
;; After deleting the file, the descriptor is not valid anymore.
(should-not (file-notify-valid-p file-notify--test-desc))
(file-notify-rm-watch file-notify--test-desc)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
;; On emba, `deleted' and `stopped' events of the directory are
;; not detected.
(unless (getenv "EMACS_EMBA_CI")
(let ((file-notify--test-tmpdir
(make-temp-file "file-notify-test-parent" t)))
(should
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpdir
'(change) #'file-notify--test-event-handler)))
(should (file-notify-valid-p file-notify--test-desc))
(file-notify--test-with-actions
(cond
;; w32notify does not raise `deleted' and `stopped'
;; events for the watched directory.
((string-equal (file-notify--test-library) "w32notify")
'(created changed deleted))
;; There are two `deleted' events, for the file and for
;; the directory. Except for
;; GFam{File,Directory}Monitor, GPollFileMonitor and
;; kqueue. And GFam{File,Directory}Monitor and
;; GPollfileMonitor do not raise a `changed' event.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor GPollFileMonitor))
'(created deleted stopped))
((string-equal (file-notify--test-library) "kqueue")
'(created changed deleted stopped))
;; GKqueueFileMonitor does not report the `changed' event.
((eq (file-notify--test-monitor) 'GKqueueFileMonitor)
'(created deleted deleted stopped))
(t '(created changed deleted deleted stopped)))
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message)
(file-notify--test-wait-event)
(delete-directory file-notify--test-tmpdir 'recursive))
;; After deleting the parent directory, the descriptor must
;; not be valid anymore.
(should-not (file-notify-valid-p file-notify--test-desc))
;; w32notify doesn't generate `stopped' events when the
;; parent directory is deleted, which doesn't provide a
;; chance for filenotify.el to remove the descriptor from
;; the internal hash table it maintains. So we must remove
;; the descriptor manually.
(if (string-equal (file-notify--test-library) "w32notify")
(file-notify--rm-descriptor file-notify--test-desc))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p)))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test05-file-validity
"Check `file-notify-valid-p' via file notification for remote files.")
(ert-deftest file-notify-test06-dir-validity ()
"Check `file-notify-valid-p' for directories."
(skip-unless (file-notify--test-local-enabled))
(unwind-protect
(progn
(should
(setq file-notify--test-tmpfile
(make-temp-file "file-notify-test-parent" t)))
(should
(setq file-notify--test-desc
(file-notify-add-watch
file-notify--test-tmpfile '(change) #'ignore)))
(should (file-notify-valid-p file-notify--test-desc))
;; After removing the watch, the descriptor must not be valid
;; anymore.
(file-notify-rm-watch file-notify--test-desc)
(file-notify--test-wait-for-events
(file-notify--test-timeout)
(not (file-notify-valid-p file-notify--test-desc)))
(should-not (file-notify-valid-p file-notify--test-desc))
(delete-directory file-notify--test-tmpfile 'recursive)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
;; On emba, `deleted' and `stopped' events of the directory are
;; not detected.
(unless (getenv "EMACS_EMBA_CI")
(should
(setq file-notify--test-tmpfile
(make-temp-file "file-notify-test-parent" t)))
(should
(setq file-notify--test-desc
(file-notify-add-watch
file-notify--test-tmpfile '(change) #'ignore)))
(should (file-notify-valid-p file-notify--test-desc))
;; After deleting the directory, the descriptor must not be
;; valid anymore.
(delete-directory file-notify--test-tmpfile 'recursive)
(file-notify--test-wait-for-events
(file-notify--test-timeout)
(not (file-notify-valid-p file-notify--test-desc)))
(should-not (file-notify-valid-p file-notify--test-desc))
(if (string-equal (file-notify--test-library) "w32notify")
(file-notify--rm-descriptor file-notify--test-desc))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test06-dir-validity
"Check `file-notify-valid-p' via file notification for remote directories.")
(ert-deftest file-notify-test07-many-events ()
"Check that events are not dropped."
:tags '(:expensive-test)
(skip-unless (file-notify--test-local-enabled))
(should
(setq file-notify--test-tmpfile
(make-temp-file "file-notify-test-parent" t)))
(should
(setq file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpfile
'(change) #'file-notify--test-event-handler)))
(unwind-protect
(let ((n 10);00)
source-file-list target-file-list
(default-directory file-notify--test-tmpfile))
(dotimes (i n)
;; It matters which direction we rename, at least for
;; kqueue. This backend parses directories in alphabetic
;; order (x%d before y%d). So we rename into both directions.
(if (zerop (mod i 2))
(progn
(push (expand-file-name (format "x%d" i)) source-file-list)
(push (expand-file-name (format "y%d" i)) target-file-list))
(push (expand-file-name (format "y%d" i)) source-file-list)
(push (expand-file-name (format "x%d" i)) target-file-list)))
(file-notify--test-with-actions (make-list (+ n n) 'created)
(let ((source-file-list source-file-list)
(target-file-list target-file-list))
(while (and source-file-list target-file-list)
(file-notify--test-wait-event)
(write-region "" nil (pop source-file-list) nil 'no-message)
(file-notify--test-wait-event)
(write-region "" nil (pop target-file-list) nil 'no-message))))
(file-notify--test-with-actions
(cond
;; w32notify fires both `deleted' and `renamed' events.
((string-equal (file-notify--test-library) "w32notify")
(let (r)
(dotimes (_i n)
(setq r (append '(deleted renamed) r)))
r))
;; GFam{File,Directory}Monitor and GPollFileMonitor fire
;; `changed' and `deleted' events, sometimes in random
;; order.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor GPollFileMonitor))
(let (r)
(dotimes (_i n)
(setq r (append '(changed deleted) r)))
(cons :random r)))
(t (make-list n 'renamed)))
(let ((source-file-list source-file-list)
(target-file-list target-file-list))
(while (and source-file-list target-file-list)
(file-notify--test-wait-event)
(rename-file (pop source-file-list) (pop target-file-list) t))))
(file-notify--test-with-actions (make-list n 'deleted)
(dolist (file target-file-list)
(file-notify--test-wait-event)
(delete-file file)))
(delete-directory file-notify--test-tmpfile)
(if (or (string-equal (file-notify--test-library) "w32notify")
(getenv "EMACS_EMBA_CI"))
(file-notify--rm-descriptor file-notify--test-desc))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
;; Unpredictable failures, eg https://hydra.nixos.org/build/86016286
(file-notify--deftest-remote file-notify-test07-many-events
"Check that events are not dropped for remote directories."
(or (getenv "EMACS_HYDRA_CI") (getenv "EMACS_EMBA_CI")))
(ert-deftest file-notify-test08-backup ()
"Check that backup keeps file notification."
:tags '(:expensive-test)
(skip-unless (file-notify--test-local-enabled))
(unwind-protect
(progn
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
(write-region "any text" nil file-notify--test-tmpfile nil 'no-message)
(should
(setq file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpfile
'(change) #'file-notify--test-event-handler)))
(should (file-notify-valid-p file-notify--test-desc))
(file-notify--test-with-actions
(cond
;; GKqueueFileMonitor does not report the `changed' event.
((eq (file-notify--test-monitor) 'GKqueueFileMonitor) '())
;; There could be one or two `changed' events.
(t '((changed)
(changed changed))))
;; There shouldn't be any problem, because the file is kept.
(with-temp-buffer
(let ((buffer-file-name file-notify--test-tmpfile)
(make-backup-files t)
(backup-by-copying t)
(kept-new-versions 1)
(delete-old-versions t))
(insert "another text")
(save-buffer))))
;; After saving the buffer, the descriptor is still valid.
(should (file-notify-valid-p file-notify--test-desc))
(delete-file file-notify--test-tmpfile)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(unwind-protect
;; It doesn't work for kqueue, because we don't use an implicit
;; directory monitor.
(unless (string-equal (file-notify--test-library) "kqueue")
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message)
(should
(setq file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpfile
'(change) #'file-notify--test-event-handler)))
(should (file-notify-valid-p file-notify--test-desc))
(file-notify--test-with-actions
(cond
;; GFam{File,Directory}Monitor and GPollFileMonitor
;; report only the `changed' event.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor GPollFileMonitor))
'(changed))
;; GKqueueFileMonitor does not report the `changed' event.
((eq (file-notify--test-monitor) 'GKqueueFileMonitor)
'(renamed created))
(t '(renamed created changed)))
;; The file is renamed when creating a backup. It shall
;; still be watched.
(with-temp-buffer
(let ((buffer-file-name file-notify--test-tmpfile)
(make-backup-files t)
(backup-by-copying nil)
(backup-by-copying-when-mismatch nil)
(kept-new-versions 1)
(delete-old-versions t))
(insert "another text")
(save-buffer))))
;; After saving the buffer, the descriptor is still valid.
(should (file-notify-valid-p file-notify--test-desc))
(delete-file file-notify--test-tmpfile)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test08-backup
"Check that backup keeps file notification for remote files.")
(ert-deftest file-notify-test09-watched-file-in-watched-dir ()
"Watches a directory and a file in that directory separately.
Checks that the callbacks are only called with events with
descriptors that were issued when registering the watches. This
test caters for the situation in bug#22736 where the callback for
the directory received events for the file with the descriptor of
the file watch."
:tags (if (getenv "EMACS_EMBA_CI")
'(:expensive-test :unstable)
'(:expensive-test))
(skip-unless (file-notify--test-local-enabled))
;; A directory to be watched.
(should
(setq file-notify--test-tmpfile
(make-temp-file "file-notify-test-parent" t)))
;; A file to be watched.
(should
(setq file-notify--test-tmpfile1
(let ((file-notify--test-tmpdir file-notify--test-tmpfile))
(file-notify--test-make-temp-name))))
(write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message)
(unwind-protect
(cl-flet (;; Directory monitor.
(dir-callback (event file)
(let ((file-notify--test-desc file-notify--test-desc1))
(file-notify--test-event-handler event file)))
;; File monitor.
(file-callback (event file)
(let ((file-notify--test-desc file-notify--test-desc2))
(file-notify--test-event-handler event file))))
(should
(setq file-notify--test-desc1
(file-notify--test-add-watch
file-notify--test-tmpfile
'(change) #'dir-callback)
;; This is needed for `file-notify--test-monitor'.
file-notify--test-desc file-notify--test-desc1))
(should
(setq file-notify--test-desc2
(file-notify--test-add-watch
file-notify--test-tmpfile1
'(change) #'file-callback)))
(should (file-notify-valid-p file-notify--test-desc1))
(should (file-notify-valid-p file-notify--test-desc2))
(should-not (equal file-notify--test-desc1 file-notify--test-desc2))
(let ((n 10));0))
;; Run the test.
(file-notify--test-with-actions
;; There could be one or two `changed' events.
(list
;; cygwin.
(append
'(:random)
(make-list (/ n 2) 'changed)
(make-list (/ n 2) 'created)
(make-list (/ n 2) 'changed))
(append
'(:random)
;; Directory monitor and file monitor.
(make-list (/ n 2) 'changed)
(make-list (/ n 2) 'changed)
;; Just the directory monitor.
(make-list (/ n 2) 'created)
(make-list (/ n 2) 'changed))
(append
'(:random)
;; Directory monitor and file monitor.
(make-list (/ n 2) 'changed)
(make-list (/ n 2) 'changed)
(make-list (/ n 2) 'changed)
(make-list (/ n 2) 'changed)
;; Just the directory monitor.
(make-list (/ n 2) 'created)
(make-list (/ n 2) 'changed))
(append
'(:random)
;; Just the directory monitor. GKqueueFileMonitor
;; does not report the `changed' event.
(make-list (/ n 2) 'created)))
(dotimes (i n)
(file-notify--test-wait-event)
(if (zerop (mod i 2))
(write-region
"any text" nil file-notify--test-tmpfile1 t 'no-message)
(let ((file-notify--test-tmpdir file-notify--test-tmpfile))
(write-region
"any text" nil
(file-notify--test-make-temp-name) nil 'no-message))))))
;; If we delete the file, the directory monitor shall still be
;; active. We receive the `deleted' event from both the
;; directory and the file monitor. The `stopped' event is
;; from the file monitor. It's undecided in which order the
;; directory and the file monitor are triggered.
(file-notify--test-with-actions '(:random deleted deleted stopped)
(delete-file file-notify--test-tmpfile1))
(should (file-notify-valid-p file-notify--test-desc1))
(should-not (file-notify-valid-p file-notify--test-desc2))
;; Now we delete the directory.
(file-notify--test-with-actions
(cond
;; GFam{File,Directory}Monitor, GPollFileMonitor and
;; kqueue raise just one `deleted' event for the
;; directory.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor GPollFileMonitor))
'(deleted stopped))
((string-equal (file-notify--test-library) "kqueue")
'(deleted stopped))
(t (append
;; The directory monitor raises a `deleted' event for
;; every file contained in the directory, we must
;; count them.
(make-list
(length
(directory-files
file-notify--test-tmpfile nil
directory-files-no-dot-files-regexp 'nosort))
'deleted)
;; The events of the directory itself.
(cond
;; w32notify does not raise `deleted' and `stopped'
;; events for the watched directory.
((string-equal (file-notify--test-library) "w32notify")
'())
;; On emba, `deleted' and `stopped' events of the
;; directory are not detected.
((getenv "EMACS_EMBA_CI")
'())
(t '(deleted stopped))))))
(delete-directory file-notify--test-tmpfile 'recursive))
(unless (getenv "EMACS_EMBA_CI")
(should-not (file-notify-valid-p file-notify--test-desc1))
(should-not (file-notify-valid-p file-notify--test-desc2)))
(when (or (string-equal (file-notify--test-library) "w32notify")
(getenv "EMACS_EMBA_CI"))
(file-notify--rm-descriptor file-notify--test-desc1)
(file-notify--rm-descriptor file-notify--test-desc2))
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test09-watched-file-in-watched-dir
"Check `file-notify-test09-watched-file-in-watched-dir' for remote files." t)
(ert-deftest file-notify-test10-sufficient-resources ()
"Check that file notification does not use too many resources."
:tags '(:expensive-test)
(skip-unless (file-notify--test-local-enabled))
;; This test is intended for kqueue only.
(skip-unless (string-equal (file-notify--test-library) "kqueue"))
(should
(setq file-notify--test-tmpfile
(make-temp-file "file-notify-test-parent" t)))
(unwind-protect
(let ((file-notify--test-tmpdir file-notify--test-tmpfile)
descs)
(should-error
(while t
;; We watch directories, because we want to reach the upper
;; limit. Watching a file might not be sufficient, because
;; most of the libraries implement this as watching the
;; upper directory.
(setq file-notify--test-tmpfile1
(make-temp-file "file-notify-test-parent" t)
descs
(cons
(should
(file-notify-add-watch
file-notify--test-tmpfile1 '(change) #'ignore))
descs)))
:type 'file-notify-error)
;; Remove watches. If we don't do it prior removing
;; directories, Emacs crashes in batch mode.
(dolist (desc descs)
(file-notify-rm-watch desc))
;; Remove directories.
(delete-directory file-notify--test-tmpfile 'recursive)
;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test10-sufficient-resources
"Check `file-notify-test10-sufficient-resources' for remote files.")
(ert-deftest file-notify-test11-symlinks ()
"Check that file notification do not follow symbolic links."
:tags '(:expensive-test)
(skip-unless (file-notify--test-local-enabled))
;; This test does not work for kqueue (yet).
(skip-when (string-equal (file-notify--test-library) "kqueue"))
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
file-notify--test-tmpfile1 (file-notify--test-make-temp-name))
;; Symlink a file.
(unwind-protect
(progn
(write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message)
;; Some systems, like MS Windows without sufficient
;; privileges, do not allow creation of symbolic links.
(condition-case nil
(make-symbolic-link
file-notify--test-tmpfile1 file-notify--test-tmpfile)
(error (ert-skip "`make-symbolic-link' not supported")))
(should
(setq file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpfile
'(attribute-change change) #'file-notify--test-event-handler)))
(should (file-notify-valid-p file-notify--test-desc))
;; Writing to either the symlink or the target should not
;; raise any event.
(file-notify--test-with-actions nil
(write-region
"another text" nil file-notify--test-tmpfile nil 'no-message)
(write-region
"another text" nil file-notify--test-tmpfile1 nil 'no-message))
;; Sanity check.
(file-notify--test-wait-for-events
(file-notify--test-timeout)
(not (input-pending-p)))
(should-not file-notify--test-events)
;; Changing timestamp of the target should not raise any
;; event. We don't use `nofollow'.
(file-notify--test-with-actions nil
(set-file-times file-notify--test-tmpfile1 '(0 0))
(set-file-times file-notify--test-tmpfile '(0 0)))
;; Sanity check.
(file-notify--test-wait-for-events
(file-notify--test-timeout)
(not (input-pending-p)))
(should-not file-notify--test-events)
;; Changing timestamp of the symlink shows the event.
(file-notify--test-with-actions
(cond
;; w32notify does not distinguish between `changed' and
;; `attribute-changed'.
((string-equal (file-notify--test-library) "w32notify")
'(changed))
;; GFam{File,Directory}Monitor, GKqueueFileMonitor and
;; GPollFileMonitor do not report the `attribute-changed'
;; event.
((memq (file-notify--test-monitor)
'(GFamFileMonitor GFamDirectoryMonitor
GKqueueFileMonitor GPollFileMonitor))
'())
(t '(attribute-changed)))
(set-file-times file-notify--test-tmpfile '(0 0) 'nofollow))
;; Deleting the target should not raise any event.
(file-notify--test-with-actions nil
(delete-file file-notify--test-tmpfile1)
(delete-file file-notify--test-tmpfile))
;; Sanity check.
(file-notify--test-wait-for-events
(file-notify--test-timeout)
(not (input-pending-p)))
(should-not file-notify--test-events)
;; The environment shall be cleaned up.
(file-notify-rm-watch file-notify--test-desc)
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup))
(setq file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
file-notify--test-tmpfile (file-notify--test-make-temp-name))
;; Symlink a directory.
(unwind-protect
(let ((tmpfile (expand-file-name "foo" file-notify--test-tmpfile))
(tmpfile1 (expand-file-name "foo" file-notify--test-tmpfile1)))
(make-directory file-notify--test-tmpfile1)
(make-symbolic-link file-notify--test-tmpfile1 file-notify--test-tmpfile)
(write-region "any text" nil tmpfile1 nil 'no-message)
(should
(setq file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpfile
'(attribute-change change) #'file-notify--test-event-handler)))
(should (file-notify-valid-p file-notify--test-desc))
;; None of the actions on a file in the symlinked directory
;; will be reported.
(file-notify--test-with-actions nil
(write-region "another text" nil tmpfile nil 'no-message)
(write-region "another text" nil tmpfile1 nil 'no-message)
(set-file-times tmpfile '(0 0))
(set-file-times tmpfile '(0 0) 'nofollow)
(set-file-times tmpfile1 '(0 0))
(set-file-times tmpfile1 '(0 0) 'nofollow)
(delete-file tmpfile)
(delete-file tmpfile1))
;; Sanity check.
(file-notify--test-wait-for-events
(file-notify--test-timeout)
(not (input-pending-p)))
(should-not file-notify--test-events)
;; The environment shall be cleaned up.
(delete-directory file-notify--test-tmpdir 'recursive)
(file-notify-rm-watch file-notify--test-desc)
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test11-symlinks
"Check `file-notify-test11-symlinks' for remote files.")
(ert-deftest file-notify-test12-unmount ()
"Check that file notification stop after unmounting the filesystem."
:tags '(:expensive-test)
(skip-unless (file-notify--test-local-enabled))
;; This test does not work for w32notify.
(skip-when (string-equal (file-notify--test-library) "w32notify"))
(unwind-protect
(progn
(setq file-notify--test-tmpfile (file-notify--test-make-temp-name))
;; File monitors like kqueue insist, that the watched file
;; exists. Directory monitors are not bound to this
;; restriction.
(when (string-equal (file-notify--test-library) "kqueue")
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message))
(should
(setq file-notify--test-desc
(file-notify--test-add-watch
file-notify--test-tmpfile
'(attribute-change change) #'file-notify--test-event-handler)))
(should (file-notify-valid-p file-notify--test-desc))
;; Unmounting the filesystem should stop watching.
(file-notify--test-with-actions '(stopped)
;; We emulate unmounting by calling
;; `file-notify-handle-event' with a corresponding event.
(file-notify-handle-event
(make-file-notify
:-event
(list file-notify--test-desc
(pcase (file-notify--test-library)
((or "inotify" "inotifywait") '(unmount isdir))
((or "gfilenotify" "gio") '(unmounted))
("kqueue" '(revoke))
(err (ert-fail (format "Library %s not supported" err))))
(pcase (file-notify--test-library)
("kqueue" (file-local-name file-notify--test-tmpfile))
(_ (file-local-name file-notify--test-tmpdir)))
;; In the inotify case, there is a 4th slot `cookie'.
;; Since it is unused for `unmount', we ignore it.
)
:-callback
(pcase (file-notify--test-library)
("inotify" #'file-notify--callback-inotify)
("gfilenotify" #'file-notify--callback-gfilenotify)
("kqueue" #'file-notify--callback-kqueue)
((or "inotifywait" "gio") #'file-notify-callback)
(err (ert-fail (format "Library %s not supported" err)))))))
;; The watch has been stopped.
(should-not (file-notify-valid-p file-notify--test-desc))
;; The environment shall be cleaned up.
(when (string-equal (file-notify--test-library) "kqueue")
(delete-file file-notify--test-tmpfile))
(file-notify--test-cleanup-p))
;; Cleanup.
(file-notify--test-cleanup)))
(file-notify--deftest-remote file-notify-test12-unmount
"Check `file-notify-test12-unmount' for remote files.")
(defun file-notify-test-all (&optional interactive)
"Run all tests for \\[file-notify]."
(interactive "p")
(if interactive
(ert-run-tests-interactively "^file-notify-")
(ert-run-tests-batch "^file-notify-")))
;; TODO:
;; * kqueue does not send all expected `deleted' events. Maybe due to
;; the missing directory monitor.
;; * For w32notify, no `deleted' and `stopped' events arrive when a
;; directory is removed.
;; * For cygwin and w32notify, no `attribute-changed' events arrive.
;; They send `changed' events instead.
;; * cygwin does not send all expected `changed' and `deleted' events.
;; Probably due to timing issues.
(provide 'file-notify-tests)
;;; filenotify-tests.el ends here
|