1 // Copyright (C) 2006 Timothy Brownawell <tbrownaw@gmail.com>
2 // 2007 Zack Weinberg <zackw@panix.com>
3 //
4 // This program is made available under the GNU GPL version 2.0 or
5 // greater. See the accompanying file COPYING for details.
6 //
7 // This program is distributed WITHOUT ANY WARRANTY; without even the
8 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9 // PURPOSE.
10
11 // Tester-specific platform interface glue, Windows version.
12
13 #define WIN32_LEAN_AND_MEAN // we don't need the GUI interfaces
14
15 #include "../../../src/base.hh"
16 #include "../tester-plaf.hh"
17 #include "../../../src/sanity.hh"
18 #include "../../../src/platform.hh"
19
20 #include <windows.h>
21
get_last_write_time(char const * name)22 time_t get_last_write_time(char const * name)
23 {
24 HANDLE h = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL,
25 OPEN_EXISTING, 0, NULL);
26 E(h != INVALID_HANDLE_VALUE, origin::system,
27 F("CreateFile(%s) failed: %s") % name % os_strerror(GetLastError()));
28
29 FILETIME ft;
30 E(GetFileTime(h, NULL, NULL, &ft), origin::system,
31 F("GetFileTime(%s) failed: %s") % name % os_strerror(GetLastError()));
32
33 CloseHandle(h);
34
35 // A FILETIME is a 64-bit quantity (represented as a pair of DWORDs)
36 // representing the number of 100-nanosecond intervals elapsed since
37 // 12:00 AM, January 1, 1601 UTC. A time_t is the same as it is for
38 // Unix: seconds since 12:00 AM, January 1, 1970 UTC. The offset is
39 // taken verbatim from MSDN.
40 LONGLONG ft64 = (((LONGLONG)ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
41 return (time_t)((ft64/10000000) - 11644473600LL);
42 }
43
do_copy_file(std::string const & from,std::string const & to)44 void do_copy_file(std::string const & from, std::string const & to)
45 {
46 // For once something is easier with Windows.
47 E(CopyFile(from.c_str(), to.c_str(), true), origin::system,
48 F("copy %s to %s: %s") % from % to % os_strerror(GetLastError()));
49 }
50
51
set_env(char const * var,char const * val)52 void set_env(char const * var, char const * val)
53 {
54 SetEnvironmentVariable(var, val);
55 }
56
unset_env(char const * var)57 void unset_env(char const * var)
58 {
59 SetEnvironmentVariable(var, 0);
60 }
61
do_umask(int)62 int do_umask(int /* mask */)
63 {
64 return -1; // not a meaningful operation on Windows
65 }
66
make_temp_dir()67 char * make_temp_dir()
68 {
69 // PATH_MAX isn't available everywhere, while FILENAME_MAX is supposed
70 // to be, in C89 and on. However, the GNU C Library manual says this:
71 //
72 // Macro: int FILENAME_MAX
73 // The value of this macro is an integer constant expression that
74 // represents the maximum length of a file name string. It is defined
75 // in `stdio.h'.
76 //
77 // Unlike PATH_MAX, this macro is defined even if there is no actual
78 // limit imposed. In such a case, its value is typically a very large
79 // number. This is always the case on the GNU system.
80 //
81 // Usage Note: Don't use FILENAME_MAX as the size of an array in which
82 // to store a file name! You can't possibly make an array that big!
83 // Use dynamic allocation (see section 3.2 Allocating Storage For
84 // Program Data) instead.
85 //
86 // So, to make sure we don't exceed resources, make sure to use a value
87 // no larger than 16384 if FILENAME_MAX is larger.
88 #if defined(PATH_MAX)
89 # define DIR_MAX_SIZE PATH_MAX
90 #elif defined(FILENAME_MAX)
91 # if FILENAME_MAX > 16384
92 # define DIR_MAX_SIZE 16384
93 # else
94 # define DIR_MAX_SIZE FILENAME_MAX
95 # endif
96 #endif
97 char dir[DIR_MAX_SIZE];
98
99 // GetTempFileName wants 14 characters at the end of the path.
100 {
101 DWORD ret = GetTempPath(DIR_MAX_SIZE - 14, dir);
102 E(ret > 0 && ret <= DIR_MAX_SIZE - 14, origin::system,
103 F("GetTempPath failed: %s") % os_strerror(GetLastError()));
104 }
105
106 // If the third argument to GetTempFileName is zero, it will create a
107 // file, which is not what we want.
108 UINT base = GetTickCount();
109 char * name = new char[DIR_MAX_SIZE];
110 for (UINT i = 0; i < 65535; i++)
111 {
112 if (base + i == 0)
113 continue;
114
115 E(GetTempFileName(dir, "MTN", base + i, name) != 0, origin::system,
116 F("GetTempFileName failed: %s") % os_strerror(GetLastError()));
117
118 if (CreateDirectory(name, NULL))
119 return name;
120
121 E(GetLastError() == ERROR_ALREADY_EXISTS, origin::system,
122 F("CreateDirectory(%s) failed: %s") % name % GetLastError());
123 }
124 E(false, origin::system, F("All temporary directory names are already in use."));
125 }
126
running_as_root()127 bool running_as_root()
128 {
129 // ??? check for privileges (what the test suite cares about is being able
130 // to create files it cannot write to - may not be impossible for any
131 // privileged account in Windows)
132 return false;
133 }
134
135 // FIXME: I don't know any intrinsic reason why parallel test cases and the
136 // jobserver protocol could not be supported on Windows (see the lengthy
137 // explanation of the protocol in unix/tester-plaf.cc) but someone with a
138 // deep understanding of Win32 would have to implement it to ensure its
139 // race-free-ness. (Before bothering, confirm that GNU Make supports the
140 // jobserver protocol on Windows.)
141 //
142 // NOTE TO POTENTIAL FIXERS: If you code this with the fake POSIX layer in
143 // the C runtime instead of with WaitForMultipleObjects and other kernel
144 // primitives, you will suffer the curse of the vengeful ghost of Dave Cutler.
145
prepare_for_parallel_testcases(int jobs,int,int)146 void prepare_for_parallel_testcases(int jobs, int, int)
147 {
148 if (jobs != 1)
149 W(F("parallel execution of test cases is not supported on Windows."));
150 }
151
152 // General note: the magic numbers in this function are meaningful to
153 // testlib.lua. They indicate a number of failure scenarios in which
154 // more detailed diagnostics are not possible.
155 // The bulk of the work is done in main(), -r case, q.v.
156
run_tests_in_children(test_enumerator const & next_test,test_invoker const &,test_cleaner const & cleanup,std::string const & run_dir,std::string const & runner,std::string const & testfile,std::string const & firstdir)157 void run_tests_in_children(test_enumerator const & next_test,
158 test_invoker const & /*invoke*/,
159 test_cleaner const & cleanup,
160 std::string const & run_dir,
161 std::string const & runner,
162 std::string const & testfile,
163 std::string const & firstdir)
164 {
165 char const * argv[6];
166 argv[0] = runner.c_str();
167 argv[1] = "-r";
168 argv[2] = testfile.c_str();
169 argv[3] = firstdir.c_str();
170 argv[4] = 0;
171 argv[5] = 0;
172
173 test_to_run t;
174 std::string testdir;
175 while (next_test(t))
176 {
177 // This must be done before we try to redirect stdout/err to a
178 // file within testdir.
179 try
180 {
181 testdir = run_dir + "/" + t.name;
182 do_remove_recursive(testdir);
183 do_mkdir(testdir);
184 }
185 catch (...)
186 {
187 cleanup(t, 121, 0, 0);
188 continue;
189 }
190
191 change_current_working_dir(testdir);
192 argv[4] = t.name.c_str();
193 pid_t child = process_spawn(argv);
194 DWORD start_millis = GetTickCount();
195 change_current_working_dir(run_dir);
196
197 int status;
198 if (child == -1)
199 status = 122;
200 else
201 process_wait(child, &status);
202
203 DWORD end_millis = GetTickCount();
204 if (cleanup(t, status, (end_millis - start_millis) / 1000, -1))
205 try
206 {
207 do_remove_recursive(testdir);
208 }
209 catch (...)
210 {
211 // process_wait sometimes returns before releasing the lock on
212 // the directory that we tried to remove. So wait a little
213 // longer and try again.
214 Sleep (1000);// milliseconds
215 do_remove_recursive(testdir);
216 }
217 }
218 }
219
220 // Local Variables:
221 // mode: C++
222 // fill-column: 76
223 // c-file-style: "gnu"
224 // indent-tabs-mode: nil
225 // End:
226 // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
227