Skip to content

Commit

Permalink
Updated version of the fstest article.
Browse files Browse the repository at this point in the history
  • Loading branch information
gwynforthewyn committed Mar 28, 2024
1 parent 41c6817 commit 786137a
Showing 1 changed file with 54 additions and 30 deletions.
84 changes: 54 additions & 30 deletions website/articles/fstest-mapfs-file-does-not-exist.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,54 @@
<main>
<article>
<section id="introduction">
<h1>The problem</h1>
<p>
In my tests, I defined an <code>fstest.MapFS</code> like this
I rewrote my tests to use an <code>fstest.MapFS</code>, defined like this
<code>
<pre>
contentRoot := fstest.MapFS{
"/parentDir/index.html": &fstest.MapFile{Data: []byte("content"), Mode: 0o755},
"/parentDir/childDir/1-2-3.html": &fstest.MapFile{Data: []byte("other content"`), Mode: 0o755},
}
</pre>
<pre>contentRoot := fstest.MapFS{
"/parentDir/index.html": &fstest.MapFile{Data: []byte("content"), Mode: 0o755},
"/parentDir/childDir/1-2-3.html": &fstest.MapFile{Data: []byte("other content"`), Mode: 0o755},
}</pre>
</code>
Simple, and yet when I ran a test against it, I received this error:

Simple, and yet when I ran a test against it, I received this error:
<code>
<pre>2024/03/27 19:42:59 http: panic serving [::1]:57135: open parentDir: file does not exist</pre>
</code>
</p>
</section>

<section id="problem-details">
<h1>The setup</h1>
<p>
I wrote this comment a few days ago and haven't thought much about it since:
<code>
<pre>
2024/03/27 19:42:59 http: panic serving [::1]:57135: open parentDir: file does not exist
</pre>
<pre>
// /index.html becomes index.html
// /articles/page.html becomes articles/page.html
// without this the paths aren't found properly inside the fs.
pagePath = strings.TrimPrefix(pagePath, "/")
pageContent, err := fs.ReadFile(a.SiteFiles, pagePath)</pre>
</code>

pagePath is retrieved from an http GET, and <i>always</i> starts with a "/"; I don't want to be seeking for an absolute
path inside the os.DirFS because the file won't be found.
</p>

<p>
ReadFile dispatches to the a.SiteFiles implementation of the Open function, so the <code>os.DirFS</code> controls lookup of the file. You
can see that <a href="https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/io/fs/readfile.go;l=37">in the official source</a>:
<code>
<pre>func ReadFile(fsys FS, name string) ([]byte, error) {
if fsys, ok := fsys.(ReadFileFS); ok {
return fsys.ReadFile(name)
}

file, err := fsys.Open(name)</pre>
</code>
</p>

<p>

</p>
</section>
Expand All @@ -43,31 +75,23 @@ <h1>What's the misunderstanding?</h1>
This one took me a couple of hours to finally understand.
</p>
<p>
An <code>fstest.MapFS</code> is pretty much a hash map. <i>If you don't have a precise match for the key in the map
then you get a <code>file does not exist</code> error.</i> Here's the implementation that shows that:
An <code>fstest.MapFS</code> is a hash map. Its implementation of <code>Open()</code> is very simple:
<code>
<pre>
file := fsys[name]
</pre>
- reference in <a href="https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/testing/fstest/mapfs.go;l=51">the go source code</a>.
<pre>file := fsys[name]</pre>
- reference in <a href="https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/testing/fstest/mapfs.go;l=51">the go source code</a>.
</code>

<i>It simply looks up the key in the hashmap, and I was manipulating that key to remove a leading slash.</i>
</p>
<h1>What's the fix?</h1>
<p>

In my case, I was performing some path munging in my logic so that I was searching for paths relatively, not absolutely
using a leading "/", so my search string was "parentDir/index.html".
</p>
<p>
Once I understood that <i>the path in the <code>fstest.MapFS</code> does not need to be an absolute path</i>, because <i>it is only
a key in a hash map</i>, I redefined my structure like this and got my tests passing:
Once I understood that <i>the path in the <code>fstest.MapFS</code> is only a map key</i>, I realised it does not need to be an absolute path.
I redefined it like this and got my tests passing:
<code>
<pre>
contentRoot := fstest.MapFS{
"/parentDir/index.html": &fstest.MapFile{Data: []byte("content"), Mode: 0o755},
"/parentDir/childDir/1-2-3.html": &fstest.MapFile{Data: []byte("other content"`), Mode: 0o755},
}
</pre>
<pre>contentRoot := fstest.MapFS{
"parentDir/index.html": &fstest.MapFile{Data: []byte("content"), Mode: 0o755},
"parentDir/childDir/1-2-3.html": &fstest.MapFile{Data: []byte("other content"`), Mode: 0o755},
}</pre>
</code>
</p>
</section>
Expand Down

0 comments on commit 786137a

Please sign in to comment.