1// Copyright 2011 Evan Shaw. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// These tests are adapted from gommap: http://labix.org/gommap
6// Copyright (c) 2010, Gustavo Niemeyer <gustavo@niemeyer.net>
7
8package mmap
9
10import (
11	"bytes"
12	"io/ioutil"
13	"os"
14	"path/filepath"
15	"testing"
16)
17
18var testData = []byte("0123456789ABCDEF")
19var testPath = filepath.Join(os.TempDir(), "testdata")
20
21func init() {
22	f := openFile(os.O_RDWR | os.O_CREATE | os.O_TRUNC)
23	f.Write(testData)
24	f.Close()
25}
26
27func openFile(flags int) *os.File {
28	f, err := os.OpenFile(testPath, flags, 0644)
29	if err != nil {
30		panic(err.Error())
31	}
32	return f
33}
34
35func TestUnmap(t *testing.T) {
36	f := openFile(os.O_RDONLY)
37	defer f.Close()
38	mmap, err := Map(f, RDONLY, 0)
39	if err != nil {
40		t.Errorf("error mapping: %s", err)
41	}
42	if err := mmap.Unmap(); err != nil {
43		t.Errorf("error unmapping: %s", err)
44	}
45}
46
47func TestReadWrite(t *testing.T) {
48	f := openFile(os.O_RDWR)
49	defer f.Close()
50	mmap, err := Map(f, RDWR, 0)
51	if err != nil {
52		t.Errorf("error mapping: %s", err)
53	}
54	defer mmap.Unmap()
55	if !bytes.Equal(testData, mmap) {
56		t.Errorf("mmap != testData: %q, %q", mmap, testData)
57	}
58
59	mmap[9] = 'X'
60	mmap.Flush()
61
62	fileData, err := ioutil.ReadAll(f)
63	if err != nil {
64		t.Errorf("error reading file: %s", err)
65	}
66	if !bytes.Equal(fileData, []byte("012345678XABCDEF")) {
67		t.Errorf("file wasn't modified")
68	}
69
70	// leave things how we found them
71	mmap[9] = '9'
72	mmap.Flush()
73}
74
75func TestProtFlagsAndErr(t *testing.T) {
76	f := openFile(os.O_RDONLY)
77	defer f.Close()
78	if _, err := Map(f, RDWR, 0); err == nil {
79		t.Errorf("expected error")
80	}
81}
82
83func TestFlags(t *testing.T) {
84	f := openFile(os.O_RDWR)
85	defer f.Close()
86	mmap, err := Map(f, COPY, 0)
87	if err != nil {
88		t.Errorf("error mapping: %s", err)
89	}
90	defer mmap.Unmap()
91
92	mmap[9] = 'X'
93	mmap.Flush()
94
95	fileData, err := ioutil.ReadAll(f)
96	if err != nil {
97		t.Errorf("error reading file: %s", err)
98	}
99	if !bytes.Equal(fileData, testData) {
100		t.Errorf("file was modified")
101	}
102}
103
104// Test that we can map files from non-0 offsets
105// The page size on most Unixes is 4KB, but on Windows it's 64KB
106func TestNonZeroOffset(t *testing.T) {
107	const pageSize = 65536
108
109	// Create a 2-page sized file
110	bigFilePath := filepath.Join(os.TempDir(), "nonzero")
111	fileobj, err := os.OpenFile(bigFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
112	if err != nil {
113		panic(err.Error())
114	}
115
116	bigData := make([]byte, 2*pageSize, 2*pageSize)
117	fileobj.Write(bigData)
118	fileobj.Close()
119
120	// Map the first page by itself
121	fileobj, err = os.OpenFile(bigFilePath, os.O_RDONLY, 0)
122	if err != nil {
123		panic(err.Error())
124	}
125	m, err := MapRegion(fileobj, pageSize, RDONLY, 0, 0)
126	if err != nil {
127		t.Errorf("error mapping file: %s", err)
128	}
129	m.Unmap()
130	fileobj.Close()
131
132	// Map the second page by itself
133	fileobj, err = os.OpenFile(bigFilePath, os.O_RDONLY, 0)
134	if err != nil {
135		panic(err.Error())
136	}
137	m, err = MapRegion(fileobj, pageSize, RDONLY, 0, pageSize)
138	if err != nil {
139		t.Errorf("error mapping file: %s", err)
140	}
141	err = m.Unmap()
142	if err != nil {
143		t.Error(err)
144	}
145
146	m, err = MapRegion(fileobj, pageSize, RDONLY, 0, 1)
147	if err == nil {
148		t.Error("expect error because offset is not multiple of page size")
149	}
150
151	fileobj.Close()
152}
153