Skip to content

Commit 9425bf5

Browse files
committed
Fix handling of FileInfo in comparisions
1 parent 1b97b7f commit 9425bf5

File tree

4 files changed

+201
-18
lines changed

4 files changed

+201
-18
lines changed

src/main/clojure/clojure/tools/namespace/dir.clj

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
[clojure.set :as set]
1919
[clojure.string :as string])
2020
(:import (System.IO DirectoryInfo FileSystemInfo Path) (System.Text.RegularExpressions Regex))) ;;; (java.io File) (java.util.regex Pattern)
21+
2122
(declare make-dir-info)
23+
2224
(defn- find-files [dirs platform]
2325
(->> dirs
2426
(map make-dir-info) ;;; (map io/file)
@@ -27,18 +29,24 @@
2729
(mapcat #(find/find-sources-in-dir % platform))
2830
)) ;;; ditto: (map #(.getCanonicalFile ^File %))
2931

32+
(defn- milliseconds-since-epoch [^DateTime time]
33+
(long
34+
(/ (- (.-Ticks time)
35+
(.-Ticks DateTime/UnixEpoch))
36+
TimeSpan/TicksPerMillisecond)))
37+
3038
(defn- modified-files [tracker files]
31-
(filter #(DateTime/op_LessThan ^DateTime (::time tracker 0) (.LastWriteTimeUTC ^FileSystemInfo %)) files)) ;;; (.lastModified ^File %)
39+
(filter #(< (::time tracker 0) (milliseconds-since-epoch (.-LastWriteTimeUtc ^FileSystemInfo %))) files)) ;;; #(< (::time tracker 0) (.lastModified ^File %))
3240

3341
(defn- deleted-files [tracker files]
34-
(set/difference (::files tracker #{}) (set files)))
42+
(set (remove #(file/some-file files %) (::files tracker #{})))) ;;; (set/difference (::files tracker #{}) (set files))
3543

3644
(defn- update-files [tracker deleted modified {:keys [read-opts]}]
37-
(let [now (DateTime/UtcNow)] ;;; (System/currentTimeMillis)
45+
(let [now (milliseconds-since-epoch DateTime/UtcNow)] ;;; (System/currentTimeMillis)
3846
(-> tracker
3947
(update-in [::files] #(if % (apply disj % deleted) #{}))
4048
(file/remove-files deleted)
41-
(update-in [::files] into modified)
49+
(update-in [::files] file/into-files modified) ;;; (update-in [::files] into modified)
4250
(file/add-files modified read-opts)
4351
(assoc ::time now))))
4452

@@ -59,7 +67,7 @@
5967
Optional third argument is map of options:
6068
6169
:platform Either clj (default) or cljs, both defined in
62-
clojure.tools.namespace.find, controls reader options for
70+
clojure.tools.namespace.find, controls reader options for
6371
parsing files.
6472
6573
:add-all? If true, assumes all extant files are modified regardless
@@ -85,8 +93,8 @@
8593
8694
Optional third argument is map of options:
8795
88-
:platform Either clj (default) or cljs, both defined in
89-
clojure.tools.namespace.find, controls file extensions
96+
:platform Either clj (default) or cljs, both defined in
97+
clojure.tools.namespace.find, controls file extensions
9098
and reader options.
9199
92100
:add-all? If true, assumes all extant files are modified regardless
@@ -118,14 +126,15 @@
118126
dependency tracker to reload files. If no dirs given, defaults to
119127
all directories on the classpath."
120128
{:added "0.2.0"
121-
:deprecated "0.3.0"}[tracker & dirs]
129+
:deprecated "0.3.0"}
130+
[tracker & dirs]
122131
(scan-dirs tracker dirs {:platform find/cljr :add-all? true})) ;;; find/clj -- is this correct?
123132

124133
;;; ADDED
125134

126-
(defn- make-dir-info
135+
(defn- make-dir-info
127136
^DirectoryInfo [x]
128-
(cond
137+
(cond
129138
(instance? DirectoryInfo x) x
130139
(string? x) (DirectoryInfo. ^String x)
131140
:default (DirectoryInfo. (str x))))

src/main/clojure/clojure/tools/namespace/file.clj

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,38 @@
6767
read by the Clojure (JVM) compiler."
6868
[^System.IO.FileSystemInfo file] ;;; java.io.File
6969
(file-with-extension? file clojure-clr-extensions))
70+
71+
;; Dealing with FileInfo.Equals is reference-based, not structuaral -- via Brandon Correa
72+
73+
(defn- files= [file-1 file-2]
74+
(= (.-FullName file-1) (.-FullName file-2)))
75+
76+
(defn some-file [coll file]
77+
(reduce #(when (files= file %2) (reduced %2)) nil coll))
7078

79+
(defn into-files [files others]
80+
(into files (remove #(some-file files %) others)))
81+
82+
(defn- dissoc-files [m files]
83+
(when m
84+
(select-keys m (remove #(some-file files %) (keys m)))))
85+
86+
(defn- get-file [filemap file]
87+
(reduce #(when (files= file (first %2)) (reduced (second %2))) nil filemap))
88+
89+
(defn- files->symbols [tracker files]
90+
(let [filemap (::filemap tracker {})]
91+
(keep #(get-file filemap %) files)))
92+
93+
(defn- merge-file-map [m other]
94+
(merge (dissoc-files m (keys other)) other))
95+
96+
(defn- distinct-files [files]
97+
(reduce #(-> (disj %1 (some-file %1 %2))
98+
(conj %2)) #{} files))
99+
100+
;;
101+
71102
;;; Dependency tracker
72103

73104
(defn- files-and-deps [files read-opts]
@@ -79,9 +110,10 @@
79110
(assoc-in [:depmap name] deps)
80111
(assoc-in [:filemap file] name)))
81112
m))
82-
{} files))
83-
84-
(def ^:private merge-map (fnil merge {}))
113+
{} (distinct-files files))) ;;; files
114+
115+
116+
(def ^:private merge-map (fnil merge-file-map {})) ;;; (fnil merge {})
85117

86118
(defn add-files
87119
"Reads ns declarations from files; returns an updated dependency
@@ -93,21 +125,20 @@
93125
(let [{:keys [depmap filemap]} (files-and-deps files read-opts)]
94126
(-> tracker
95127
(track/add depmap)
96-
(update-in [::filemap] merge-map filemap)))))
128+
(update ::filemap merge-map filemap))))) ;;; (update-in [::filemap] merge-map filemap)
97129

98130
(defn remove-files
99131
"Returns an updated dependency tracker with files removed. The files
100132
must have been previously added with add-files."
101133
[tracker files]
102134
(-> tracker
103-
(track/remove (keep (::filemap tracker {}) files))
104-
(update-in [::filemap] #(apply dissoc % files))))
135+
(track/remove (files->symbols tracker files)) ;;; (track/remove (keep (::filemap tracker {}) files))
136+
(update ::filemap dissoc-files files))) ;;; (update-in [::filemap] #(apply dissoc % files))
105137

106138
;;; Added
107139

108140
(defn is-file? [^System.IO.FileSystemInfo file]
109141
(not= (enum-and (.Attributes file) System.IO.FileAttributes/Directory) System.IO.FileAttributes/Directory))
110-
142+
111143
(defn is-directory? [^System.IO.FileSystemInfo file]
112144
(= (enum-and (.Attributes file) System.IO.FileAttributes/Directory) System.IO.FileAttributes/Directory))
113-
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
(ns clojure.tools.namespace.file-test
2+
(:require [clojure.test :refer [deftest is]]
3+
[clojure.tools.namespace.dependency :as dep]
4+
[clojure.tools.namespace.file :as file]
5+
[clojure.tools.namespace.test-helpers :as help]
6+
[clojure.tools.namespace.track :as track])
7+
(:import (System.IO FileInfo)))
8+
9+
;; Tests compliments of Brandon Correa
10+
11+
(deftest t-add-no-files
12+
(let [tracker (file/add-files (track/tracker) nil)]
13+
(is (= (dep/->MapDependencyGraph {} {}) (::track/deps tracker)))
14+
(is (= {} (::file/filemap tracker)))
15+
(is (= '() (::track/unload tracker)))
16+
(is (= '() (::track/load tracker)))))
17+
18+
(deftest t-add-one-file
19+
(let [dir (help/create-temp-dir "t-add-one-file")
20+
one-clj (help/create-source dir 'example.one :clj)
21+
tracker (file/add-files (track/tracker) [one-clj])]
22+
(is (= (dep/->MapDependencyGraph {} {}) (::track/deps tracker)))
23+
(is (= {one-clj 'example.one} (::file/filemap tracker)))
24+
(is (= (list 'example.one) (::track/unload tracker)))
25+
(is (= (list 'example.one) (::track/load tracker)))))
26+
27+
(deftest t-add-file-with-dependency
28+
(let [dir (help/create-temp-dir "t-add-file-with-dependency")
29+
main-clj (help/create-source dir 'example.main :clj '[example.one])
30+
tracker (file/add-files (track/tracker) [main-clj])]
31+
(is (= {'example.main #{'example.one}} (:dependencies (::track/deps tracker))))
32+
(is (= {'example.one #{'example.main}} (:dependents (::track/deps tracker))))
33+
(is (= {main-clj 'example.main} (::file/filemap tracker)))
34+
(is (= (list 'example.main) (::track/unload tracker)))
35+
(is (= (list 'example.main) (::track/load tracker)))))
36+
37+
(deftest t-add-file-that-already-exists
38+
(let [dir (help/create-temp-dir "t-add-file-that-already-exists")
39+
file-ref-1 (help/create-source dir 'example.main :clj)
40+
file-ref-2 (FileInfo. (.-FullName file-ref-1))
41+
tracker (-> (track/tracker)
42+
(file/add-files [file-ref-1])
43+
(file/add-files [file-ref-2]))]
44+
(is (= {} (:dependencies (::track/deps tracker))))
45+
(is (= {} (:dependents (::track/deps tracker))))
46+
(is (= {file-ref-2 'example.main} (::file/filemap tracker)))
47+
(is (= (list 'example.main) (::track/unload tracker)))
48+
(is (= (list 'example.main) (::track/load tracker)))))
49+
50+
(deftest t-add-file-that-already-exists-in-the-same-call
51+
(let [dir (help/create-temp-dir "t-add-file-that-already-exists-in-the-same-call")
52+
file-ref-1 (help/create-source dir 'example.main :clj)
53+
file-ref-2 (FileInfo. (.-FullName file-ref-1))
54+
tracker (-> (track/tracker)
55+
(file/add-files [file-ref-1 file-ref-2]))]
56+
(is (= {} (:dependencies (::track/deps tracker))))
57+
(is (= {} (:dependents (::track/deps tracker))))
58+
(is (= {file-ref-2 'example.main} (::file/filemap tracker)))
59+
(is (= (list 'example.main) (::track/unload tracker)))
60+
(is (= (list 'example.main) (::track/load tracker)))))
61+
62+
(deftest t-remove-no-files-from-empty-tracker
63+
(let [tracker (file/remove-files {} nil)]
64+
(is (= (dep/->MapDependencyGraph {} {}) (::track/deps tracker)))
65+
(is (nil? (::file/filemap tracker)))
66+
(is (= '() (::track/unload tracker)))
67+
(is (= '() (::track/load tracker)))))
68+
69+
(deftest t-remove-file-with-dependency-from-filemap
70+
(let [dir (help/create-temp-dir "t-remove-file-with-dependency-from-filemap")
71+
file-ref-1 (help/create-source dir 'example.main :clj '[example.one])
72+
file-ref-2 (FileInfo. (.-FullName file-ref-1))
73+
tracker (-> (track/tracker)
74+
(file/add-files [file-ref-1])
75+
(file/remove-files [file-ref-2]))]
76+
(is (= {} (:dependencies (::track/deps tracker))))
77+
(is (= {'example.one #{}} (:dependents (::track/deps tracker))))
78+
(is (= {} (::file/filemap tracker)))
79+
(is (= (list 'example.main) (::track/unload tracker)))
80+
(is (= (list) (::track/load tracker)))))
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
(ns clojure.tools.namespace.repl-test
2+
(:require [clojure.test :refer [deftest is use-fixtures]]
3+
[clojure.tools.namespace.dir :as dir]
4+
[clojure.tools.namespace.find :as find]
5+
[clojure.tools.namespace.repl :as repl]
6+
[clojure.tools.namespace.test-helpers :as help]))
7+
8+
;; Tests contributed by Brandon Correa
9+
10+
(defn reset-repl! []
11+
(repl/clear)
12+
(repl/set-refresh-dirs))
13+
14+
(defn reset-repl-fixture [test-fn]
15+
(reset-repl!)
16+
(test-fn)
17+
(reset-repl!))
18+
19+
(use-fixtures :each reset-repl-fixture)
20+
21+
(defn current-time-millis []
22+
(long
23+
(/ (- (.-Ticks DateTime/UtcNow)
24+
(.-Ticks DateTime/UnixEpoch))
25+
TimeSpan/TicksPerMillisecond)))
26+
27+
(deftest t-repl-scan-time-component
28+
(let [before (current-time-millis)
29+
scan (repl/scan {:platform find/clj})
30+
after (current-time-millis)
31+
time (::dir/time scan)]
32+
(is (<= before time after))
33+
(is (integer? (::dir/time scan)))))
34+
35+
(deftest t-repl-scan-twice
36+
(let [dir (help/create-temp-dir "t-repl-scan")
37+
other-dir (help/create-temp-dir "t-repl-scan-other")
38+
main-clj (help/create-source dir 'example.main :clj '[example.one])
39+
one-cljc (help/create-source dir 'example.one :clj)
40+
_ (repl/set-refresh-dirs dir other-dir)
41+
scan-1 (repl/scan {:platform find/clj})
42+
scan-2 (repl/scan {:platform find/clj})
43+
paths-1 (map str (::dir/files scan-1))
44+
paths-2 (map str (::dir/files scan-2))
45+
paths (set paths-1)]
46+
(is (= 2 (count paths-1)))
47+
(is (= paths-1 paths-2))
48+
(is (contains? paths (str main-clj)))
49+
(is (contains? paths (str one-cljc)))))
50+
51+
(deftest t-repl-scan-after-file-modified
52+
(let [dir (help/create-temp-dir "t-repl-scan-after-file-modified")
53+
main-clj (help/create-source dir 'example.main :clj)
54+
_ (repl/set-refresh-dirs dir)
55+
scan-1 (repl/scan {:platform find/clj})
56+
_ (System.IO.File/SetLastWriteTimeUtc (.-FullName main-clj) DateTime/UtcNow)
57+
scan-2 (repl/scan {:platform find/clj})
58+
paths-1 (map str (::dir/files scan-1))
59+
paths-2 (map str (::dir/files scan-2))
60+
paths (set paths-1)]
61+
(is (= 1 (count paths-1)))
62+
(is (= paths-1 paths-2))
63+
(is (contains? paths (str main-clj)))))

0 commit comments

Comments
 (0)