Make versaplexd unit tests pass with new ReadArray<T> stuff.
[versaplex.git] / versaplexd / t / versaplextester.cs
blob505a6b17e92fd7b03efd292dd676621cfb9dfaf0
1 #include "wvtest.cs.h"
3 using System;
4 using System.Data.SqlClient;
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Text;
8 using Wv;
9 using Wv.Test;
11 // Several mono bugs worked around in this test fixture are filed as mono bug
12 // #81940
14 public class VersaplexTester: IDisposable
16 // A file full of "lorem ipsum dolor" text
17 private const string lipsum_file = "lipsum.txt";
18 // A UTF-8 test file
19 private const string unicode_file = "UTF-8-demo.txt";
20 // A random file of binary goop
21 private const string goop_file = "random.bin";
22 // THTBACS image
23 private const string image_file = "thtbacs.tiff";
25 public WvDbi dbi;
26 protected Bus bus;
28 public VersaplexTester()
30 // Places to look for the config file.
31 string [] searchfiles =
33 "versaplexd.ini",
34 Path.Combine("..", "versaplexd.ini")
37 string cfgfile = null;
38 foreach (string searchfile in searchfiles)
39 if (File.Exists(searchfile))
40 cfgfile = searchfile;
42 if (cfgfile == null)
43 throw new Exception("Cannot locate versaplexd.ini.");
45 WvIni cfg = new WvIni(cfgfile);
47 string uname = Mono.Unix.UnixUserInfo.GetRealUser().UserName;
48 string dbname = cfg.get("User Map", uname);
49 if (dbname == null)
50 dbname = cfg.get("User Map", "*");
51 if (dbname == null)
52 throw new Exception(String.Format(
53 "User '{0}' (and '*') missing from config.", uname));
55 string cfgval = cfg.get("Connections", dbname);
56 if (cfgval == null)
57 throw new Exception(String.Format(
58 "Connection string for '{0}' missing from config.", dbname));
60 dbi = WvDbi.create(cfgval);
62 if (Address.Session == null)
63 throw new Exception ("DBUS_SESSION_BUS_ADDRESS not set");
64 AddressEntry aent = AddressEntry.Parse(Address.Session);
65 DodgyTransport trans = new DodgyTransport();
66 trans.Open(aent);
67 bus = new Bus(trans);
70 public void Dispose()
72 bus = null;
73 dbi.Dispose();
76 public static string GetTempDir()
78 WvLog log = new WvLog("GetTempDir");
79 string t = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
80 log.print("Using temporary directory " + t + "\n");
82 return t;
85 internal bool Exec(string query)
87 Console.WriteLine(" + Exec SQL Query: {0}", query);
88 dbi.exec(query);
89 return true;
92 internal bool Scalar(string query, out object result)
94 Console.WriteLine(" + Scalar SQL Query: {0}", query);
95 result = dbi.select_one(query).inner;
96 return true;
99 internal WvSqlRows Reader(string query)
101 Console.WriteLine(" + Reader SQL Query: {0}", query);
102 return dbi.select(query);
105 internal bool VxExec(string query)
107 Console.WriteLine(" + VxExec SQL Query: {0}", query);
109 Message call = VxDbusUtils.CreateMethodCall(bus, "ExecRecordset", "s");
111 MessageWriter mw = new MessageWriter(Connection.NativeEndianness);
112 mw.Write(typeof(string), query);
114 call.Body = mw.ToArray();
116 Message reply = bus.SendWithReplyAndBlock(call);
118 switch (reply.Header.MessageType) {
119 case MessageType.MethodReturn:
120 return true;
121 case MessageType.Error:
123 object errname;
124 if (!reply.Header.Fields.TryGetValue(FieldCode.ErrorName,
125 out errname))
126 throw new Exception("D-Bus error received but no error name "
127 +"given");
129 object errsig;
130 if (!reply.Header.Fields.TryGetValue(FieldCode.Signature,
131 out errsig) || errsig.ToString() != "s")
132 throw new DbusError(errname.ToString());
134 MessageReader mr = new MessageReader(reply);
136 string errmsg = mr.ReadString();
138 throw new DbusError(errname.ToString() + ": " + errmsg.ToString());
140 default:
141 throw new Exception("D-Bus response was not a method return or "
142 +"error");
146 internal bool VxScalar(string query, out object result)
148 Console.WriteLine(" + VxScalar SQL Query: {0}", query);
150 Message call = VxDbusUtils.CreateMethodCall(bus, "ExecScalar", "s");
152 MessageWriter mw = new MessageWriter(Connection.NativeEndianness);
153 mw.Write(typeof(string), query);
155 call.Body = mw.ToArray();
157 Message reply = bus.SendWithReplyAndBlock(call);
159 switch (reply.Header.MessageType) {
160 case MessageType.MethodReturn:
162 object replysig;
163 if (!reply.Header.Fields.TryGetValue(FieldCode.Signature,
164 out replysig))
165 throw new Exception("D-Bus reply had no signature");
167 if (replysig == null || replysig.ToString() != "v")
168 throw new Exception("D-Bus reply had invalid signature");
170 MessageReader reader = new MessageReader(reply);
171 result = reader.ReadVariant();
173 return true;
175 case MessageType.Error:
177 object errname;
178 if (!reply.Header.Fields.TryGetValue(FieldCode.ErrorName,
179 out errname))
180 throw new Exception("D-Bus error received but no error name "
181 +"given");
183 object errsig;
184 if (!reply.Header.Fields.TryGetValue(FieldCode.Signature,
185 out errsig) || errsig.ToString() != "s")
186 throw new DbusError(errname.ToString());
188 MessageReader mr = new MessageReader(reply);
190 object errmsg = mr.ReadString();
192 throw new DbusError(errname.ToString() + ": " + errmsg.ToString());
194 default:
195 throw new Exception("D-Bus response was not a method return or "
196 +"error");
200 // Read the standard issnny signature for column information. We can't
201 // just read a VxColumnInfo[] straight from the reader any more, as the
202 // format of VxColumnInfo differs from the format on the wire.
203 internal VxColumnInfo[] ReadColInfo(MessageReader reader)
205 List<VxColumnInfo> colinfolist = new List<VxColumnInfo>();
207 int colinfosize = reader.ReadInt32();
208 int endpos = reader.Position + colinfosize;
209 while (reader.Position < endpos)
211 reader.ReadPad(8);
212 int size = reader.ReadInt32();
213 string colname = reader.ReadString();
214 string coltype_str = reader.ReadString();
215 short precision = reader.ReadInt16();
216 short scale = reader.ReadInt16();
217 byte nullable = reader.ReadByte();
219 VxColumnType coltype = (VxColumnType)Enum.Parse(
220 typeof(VxColumnType), coltype_str, true);
222 Console.WriteLine("Read colname={0}, coltype={1}, nullable={2}, " +
223 "size={3}, precision={4}, scale={5}",
224 colname, coltype.ToString(), nullable, size, precision, scale);
226 colinfolist.Add(new VxColumnInfo(colname, coltype, nullable > 0,
227 size, precision, scale));
230 return colinfolist.ToArray();
233 internal bool VxChunkRecordset(string query, out VxColumnInfo[] colinfo,
234 out object[][]data, out bool[][] nullity)
236 Console.WriteLine(" + VxChunkRecordset SQL Query: {0}", query);
238 Message call = VxDbusUtils.CreateMethodCall(bus, "ExecChunkRecordset", "s");
239 //call.Header.Flags = HeaderFlag.NoReplyExpected | HeaderFlag.NoAutoStart;
241 MessageWriter mw = new MessageWriter(Connection.NativeEndianness);
242 mw.Write(typeof(string), query);
244 call.Body = mw.ToArray();
246 bus.Send(call);
248 colinfo = null;
249 List<object[]> rowlist = new List<object[]>();
250 List<bool[]> rownulllist = new List<bool[]>();
251 while (true)
253 object[][] tdata;
254 bool[][] tnullity;
255 Message tmp = bus.GetNext();
256 if (tmp.Header.MessageType == MessageType.Signal)
258 RecordsetWorker(tmp, out colinfo, out tdata, out tnullity);
259 rowlist.AddRange(tdata);
260 rownulllist.AddRange(tnullity);
262 else if (tmp.Header.MessageType == MessageType.Error)
264 object errname;
265 if (!tmp.Header.Fields.TryGetValue(FieldCode.ErrorName,
266 out errname))
267 throw new Exception("D-Bus error received but no error "
268 + "name given");
270 object errsig;
271 if (!tmp.Header.Fields.TryGetValue(FieldCode.Signature,
272 out errsig) || errsig.ToString() != "s")
273 throw new DbusError(errname.ToString());
275 MessageReader mr = new MessageReader(tmp);
277 string errmsg = mr.ReadString();
278 throw new DbusError(errname.ToString() + ": " + errmsg);
280 else
282 //Method return
283 object retsig;
284 if (!tmp.Header.Fields.TryGetValue(FieldCode.Signature,
285 out retsig) || retsig.ToString() != "s")
286 throw new DbusError("Garbled response for ExecChunkRecordSet");
287 //otherwise, we presume it's our method return response
288 data = rowlist.ToArray();
289 nullity = rownulllist.ToArray();
290 break;
294 return true;
297 internal bool RecordsetWorker(Message reply, out VxColumnInfo[] colinfo,
298 out object[][] data, out bool[][] nullity)
300 object replysig;
301 if (!reply.Header.Fields.TryGetValue(FieldCode.Signature,
302 out replysig))
303 throw new Exception("D-Bus reply had no signature");
305 if (replysig == null || !replysig.ToString().StartsWith("a(issnny)vaay"))
306 throw new Exception("D-Bus reply had invalid signature");
308 MessageReader reader = new MessageReader(reply);
310 // Read the column information
311 colinfo = ReadColInfo(reader);
313 reader.ReadSignature();
315 // TODO: Check that sig matches colinfo
316 // Sig should be of the form a(...)
318 int arraysz = reader.ReadInt32();
320 // The header is 8-byte aligned
321 reader.ReadPad(8);
322 int endpos = reader.Position + arraysz;
324 List<object[]> results = new List<object[]>();
325 while (reader.Position < endpos) {
326 object[] row = new object[colinfo.Length];
328 // Each structure element is 8-byte aligned
329 reader.ReadPad(8);
331 for (int i=0; i < row.Length; i++) {
332 switch (colinfo[i].VxColumnType) {
333 case VxColumnType.Int64:
335 Console.WriteLine("Reading Int64 from pos {0}",
336 reader.Position);
337 long cell = reader.ReadInt64();
338 row[i] = cell;
339 break;
341 case VxColumnType.Int32:
343 Console.WriteLine("Reading Int32 from pos {0}",
344 reader.Position);
345 int cell = reader.ReadInt32();
346 row[i] = cell;
347 break;
349 case VxColumnType.Int16:
351 Console.WriteLine("Reading Int16 from pos {0}",
352 reader.Position);
353 short cell = reader.ReadInt16();
354 row[i] = cell;
355 break;
357 case VxColumnType.UInt8:
359 Console.WriteLine("Reading UInt8 from pos {0}",
360 reader.Position);
361 byte cell = reader.ReadByte();
362 row[i] = cell;
363 break;
365 case VxColumnType.Bool:
367 Console.WriteLine("Reading Bool from pos {0}",
368 reader.Position);
369 bool cell = reader.ReadBoolean();
370 row[i] = cell;
371 break;
373 case VxColumnType.Double:
375 Console.WriteLine("Reading Double from pos {0}",
376 reader.Position);
377 double cell = reader.ReadDouble();
378 row[i] = cell;
379 break;
381 case VxColumnType.Uuid:
383 Console.WriteLine("Reading UUID from pos {0}",
384 reader.Position);
385 string cell = reader.ReadString();
387 if (cell == "") {
388 row[i] = new Guid();
389 } else {
390 row[i] = new Guid(cell);
392 break;
394 case VxColumnType.Binary:
396 Console.WriteLine("Reading Binary from pos {0}",
397 reader.Position);
398 object cell = reader.ReadArray<byte>();
399 row[i] = cell;
400 break;
402 case VxColumnType.String:
404 Console.WriteLine("Reading string from pos {0}",
405 reader.Position);
406 string cell = reader.ReadString();
407 row[i] = cell;
408 break;
410 case VxColumnType.DateTime:
412 Console.WriteLine("Reading DateTime from pos {0}",
413 reader.Position);
414 reader.ReadPad(8);
415 long seconds = reader.ReadInt64();
416 int microseconds = reader.ReadInt32();
418 VxDbusDateTime dt = new VxDbusDateTime();
419 dt.seconds = seconds;
420 dt.microseconds = microseconds;
422 row[i] = dt;
423 break;
425 case VxColumnType.Decimal:
427 Console.WriteLine("Reading Decimal from pos {0}",
428 reader.Position);
429 string cell = reader.ReadString();
431 if (cell == "") {
432 row[i] = new Decimal();
433 } else {
434 row[i] = Decimal.Parse(cell);
436 break;
438 default:
439 throw new Exception("Invalid column type received");
443 results.Add(row);
446 WVPASSEQ(reader.Position, endpos);
447 if (reader.Position != endpos)
448 throw new Exception("Position mismatch after reading data");
450 data = results.ToArray();
452 object rawnulls = reader.ReadArray<byte[]>();
454 byte[][] rawnulls_typed = (byte[][])rawnulls;
456 nullity = new bool[rawnulls_typed.Length][];
458 for (int i=0; i < rawnulls_typed.Length; i++) {
459 nullity[i] = new bool[rawnulls_typed[i].Length];
461 for (int j=0; j < rawnulls_typed[i].Length; j++) {
462 nullity[i][j] = (rawnulls_typed[i][j] == 0) ? false : true;
466 return true;
469 internal bool VxRecordset(string query, out VxColumnInfo[] colinfo,
470 out object[][] data, out bool[][] nullity)
472 Console.WriteLine(" + VxReader SQL Query: {0}", query);
474 Message call = VxDbusUtils.CreateMethodCall(bus, "ExecRecordset", "s");
476 MessageWriter mw = new MessageWriter(Connection.NativeEndianness);
477 mw.Write(typeof(string), query);
479 call.Body = mw.ToArray();
481 Message reply = bus.SendWithReplyAndBlock(call);
483 switch (reply.Header.MessageType) {
484 case MessageType.MethodReturn:
486 return RecordsetWorker(reply, out colinfo, out data, out nullity);
488 case MessageType.Error:
490 object errname;
491 if (!reply.Header.Fields.TryGetValue(FieldCode.ErrorName,
492 out errname))
493 throw new Exception("D-Bus error received but no error name "
494 +"given");
496 object errsig;
497 if (!reply.Header.Fields.TryGetValue(FieldCode.Signature,
498 out errsig) || errsig.ToString() != "s")
499 throw new DbusError(errname.ToString());
501 MessageReader mr = new MessageReader(reply);
503 string errmsg = mr.ReadString();
505 throw new DbusError(errname.ToString() + ": " + errmsg.ToString());
507 default:
508 throw new Exception("D-Bus response was not a method return or "
509 +"error");
513 internal bool Insert(string table, params object [] param)
515 Console.WriteLine(" + Insert to {0} ({1})", table, String.Join(", ",
516 wv.stringify(param)));
518 var query = new StringBuilder();
519 query.AppendFormat("INSERT INTO [{0}] VALUES (",
520 table.Replace("]","]]"));
522 for (int i=0; i < param.Length; i++)
524 if (i > 0)
525 query.Append(", ");
527 if (param[i] is DBNull)
528 query.Append("NULL");
529 else
530 query.AppendFormat("@col{0}", i);
533 query.Append(")");
535 Console.WriteLine(" ++ ({0})", query.ToString());
537 dbi.exec(query.ToString(), param);
538 return true;
541 internal string read_lipsum()
543 WVASSERT(File.Exists(lipsum_file));
545 using (StreamReader sr = new StreamReader(lipsum_file)) {
546 return sr.ReadToEnd();
550 internal string read_unicode()
552 WVASSERT(File.Exists(unicode_file));
554 using (StreamReader sr = new StreamReader(unicode_file)) {
555 return sr.ReadToEnd();
559 internal Byte [] read_goop()
561 WVASSERT(File.Exists(goop_file));
563 using (FileStream f = new FileStream(goop_file, FileMode.Open,
564 FileAccess.Read))
565 using (BinaryReader sr = new BinaryReader(f)) {
566 return sr.ReadBytes((int)Math.Min(f.Length, Int32.MaxValue));
570 internal Byte [] read_image()
572 WVASSERT(File.Exists(image_file));
574 using (FileStream f = new FileStream(image_file, FileMode.Open,
575 FileAccess.Read))
576 using (BinaryReader sr = new BinaryReader(f)) {
577 return sr.ReadBytes((int)Math.Min(f.Length, Int32.MaxValue));
581 internal long GetInt64(SqlDataReader reader, int colnum) {
582 // For some reason, it won't just up-convert int32 to int64
583 if (reader.GetFieldType(colnum) == typeof(System.Int32)) {
584 return reader.GetInt32(colnum);
585 } else if (reader.GetFieldType(colnum) == typeof(System.Int64)) {
586 return reader.GetInt64(colnum);
587 } else if (reader.GetFieldType(colnum) == typeof(System.Decimal)) {
588 return (long)reader.GetDecimal(colnum);
589 } else {
590 // Unknown type
591 bool unknown_type_in_result = true;
592 WVFAIL(unknown_type_in_result);
594 return -1;