data.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. // Property-based testing representations of various things in AMQP
  2. 'use strict';
  3. var C = require('claire');
  4. var forAll = C.forAll;
  5. var arb = C.data;
  6. var transform = C.transform;
  7. var repeat = C.repeat;
  8. var label = C.label;
  9. var sequence = C.sequence;
  10. var asGenerator = C.asGenerator;
  11. var sized = C.sized;
  12. var recursive = C.recursive;
  13. var choice = C.choice;
  14. var Undefined = C.Undefined;
  15. // Stub these out so we can use outside tests
  16. // if (!suite) var suite = function() {}
  17. // if (!test) var test = function() {}
  18. // These aren't exported in claire/index. so I could have to reproduce
  19. // them I guess.
  20. function choose(a, b) {
  21. return Math.random() * (b - a) + a;
  22. }
  23. function chooseInt(a, b) {
  24. return Math.floor(choose(a, b));
  25. }
  26. function rangeInt(name, a, b) {
  27. return label(name,
  28. asGenerator(function(_) { return chooseInt(a, b); }));
  29. }
  30. function toFloat32(i) {
  31. var b = Buffer.alloc(4);
  32. b.writeFloatBE(i, 0);
  33. return b.readFloatBE(0);
  34. }
  35. function floatChooser(maxExp) {
  36. return function() {
  37. var n = Number.NaN;
  38. while (isNaN(n)) {
  39. var mantissa = Math.random() * 2 - 1;
  40. var exponent = chooseInt(0, maxExp);
  41. n = Math.pow(mantissa, exponent);
  42. }
  43. return n;
  44. }
  45. }
  46. function explicitType(t, underlying) {
  47. return label(t, transform(function(n) {
  48. return {'!': t, value: n};
  49. }, underlying));
  50. }
  51. // FIXME null, byte array, others?
  52. var Octet = rangeInt('octet', 0, 255);
  53. var ShortStr = label('shortstr',
  54. transform(function(s) {
  55. return s.substr(0, 255);
  56. }, arb.Str));
  57. var LongStr = label('longstr',
  58. transform(
  59. function(bytes) { return Buffer.from(bytes); },
  60. repeat(Octet)));
  61. var UShort = rangeInt('short-uint', 0, 0xffff);
  62. var ULong = rangeInt('long-uint', 0, 0xffffffff);
  63. var ULongLong = rangeInt('longlong-uint', 0, 0xffffffffffffffff);
  64. var Short = rangeInt('short-int', -0x8000, 0x7fff);
  65. var Long = rangeInt('long-int', -0x80000000, 0x7fffffff);
  66. var LongLong = rangeInt('longlong-int', -0x8000000000000000,
  67. 0x7fffffffffffffff);
  68. var Bit = label('bit', arb.Bool);
  69. var Double = label('double', asGenerator(floatChooser(308)));
  70. var Float = label('float', transform(toFloat32, floatChooser(38)));
  71. var Timestamp = label('timestamp', transform(
  72. function(n) {
  73. return {'!': 'timestamp', value: n};
  74. }, ULongLong));
  75. var Decimal = label('decimal', transform(
  76. function(args) {
  77. return {'!': 'decimal', value: {places: args[1], digits: args[0]}};
  78. }, sequence(arb.UInt, Octet)));
  79. var UnsignedByte = label('unsignedbyte', transform(
  80. function(n) {
  81. return {'!': 'unsignedbyte', value: n};
  82. }, Octet));
  83. var UnsignedShort = label('unsignedshort', transform(
  84. function(n) {
  85. return {'!': 'unsignedshort', value: n};
  86. }, UShort));
  87. var UnsignedInt = label('unsignedint', transform(
  88. function(n) {
  89. return {'!': 'unsignedint', value: n};
  90. }, ULong));
  91. // Signed 8 bit int
  92. var Byte = rangeInt('byte', -128, 127);
  93. // Explicitly typed values
  94. var ExByte = explicitType('byte', Byte);
  95. var ExInt8 = explicitType('int8', Byte);
  96. var ExShort = explicitType('short', Short);
  97. var ExInt16 = explicitType('int16', Short);
  98. var ExInt = explicitType('int', Long);
  99. var ExInt32 = explicitType('int32', Long);
  100. var ExLong = explicitType('long', LongLong);
  101. var ExInt64 = explicitType('int64', LongLong);
  102. var FieldArray = label('field-array', recursive(function() {
  103. return arb.Array(
  104. arb.Null,
  105. LongStr, ShortStr,
  106. Octet, UShort, ULong, ULongLong,
  107. Byte, Short, Long, LongLong,
  108. ExByte, ExInt8, ExShort, ExInt16,
  109. ExInt, ExInt32, ExLong, ExInt64,
  110. Bit, Float, Double, FieldTable, FieldArray)
  111. }));
  112. var FieldTable = label('table', recursive(function() {
  113. return sized(function() { return 5; },
  114. arb.Object(
  115. arb.Null,
  116. LongStr, ShortStr, Octet,
  117. UShort, ULong, ULongLong,
  118. Byte, Short, Long, LongLong,
  119. ExByte, ExInt8, ExShort, ExInt16,
  120. ExInt, ExInt32, ExLong, ExInt64,
  121. Bit, Float, Double, FieldArray, FieldTable))
  122. }));
  123. // Internal tests of our properties
  124. var domainProps = [
  125. [Octet, function(n) { return n >= 0 && n < 256; }],
  126. [ShortStr, function(s) { return typeof s === 'string' && s.length < 256; }],
  127. [LongStr, function(s) { return Buffer.isBuffer(s); }],
  128. [UShort, function(n) { return n >= 0 && n <= 0xffff; }],
  129. [ULong, function(n) { return n >= 0 && n <= 0xffffffff; }],
  130. [ULongLong, function(n) {
  131. return n >= 0 && n <= 0xffffffffffffffff; }],
  132. [Short, function(n) { return n >= -0x8000 && n <= 0x8000; }],
  133. [Long, function(n) { return n >= -0x80000000 && n < 0x80000000; }],
  134. [LongLong, function(n) { return n >= -0x8000000000000000 && n < 0x8000000000000000; }],
  135. [Bit, function(b) { return typeof b === 'boolean'; }],
  136. [Double, function(f) { return !isNaN(f) && isFinite(f); }],
  137. [Float, function(f) { return !isNaN(f) && isFinite(f) && (Math.log(Math.abs(f)) * Math.LOG10E) < 309; }],
  138. [Decimal, function(d) {
  139. return d['!'] === 'decimal' &&
  140. d.value['places'] <= 255 &&
  141. d.value['digits'] <= 0xffffffff;
  142. }],
  143. [Timestamp, function(t) { return t['!'] === 'timestamp'; }],
  144. [FieldTable, function(t) { return typeof t === 'object'; }],
  145. [FieldArray, function(a) { return Array.isArray(a); }]
  146. ];
  147. suite("Domains", function() {
  148. domainProps.forEach(function(p) {
  149. test(p[0] + ' domain',
  150. forAll(p[0]).satisfy(p[1]).asTest({times: 500}));
  151. });
  152. });
  153. // For methods and properties (as opposed to field table values) it's
  154. // easier just to accept and produce numbers for timestamps.
  155. var ArgTimestamp = label('timestamp', ULongLong);
  156. // These are the domains used in method arguments
  157. var ARG_TYPES = {
  158. 'octet': Octet,
  159. 'shortstr': ShortStr,
  160. 'longstr': LongStr,
  161. 'short': UShort,
  162. 'long': ULong,
  163. 'longlong': ULongLong,
  164. 'bit': Bit,
  165. 'table': FieldTable,
  166. 'timestamp': ArgTimestamp
  167. };
  168. function argtype(thing) {
  169. if (thing.default === undefined) {
  170. return ARG_TYPES[thing.type];
  171. }
  172. else {
  173. return choice(ARG_TYPES[thing.type], Undefined);
  174. }
  175. }
  176. function zipObject(vals, names) {
  177. var obj = {};
  178. vals.forEach(function(v, i) { obj[names[i]] = v; });
  179. return obj;
  180. }
  181. function name(arg) { return arg.name; }
  182. var defs = require('../lib/defs');
  183. function method(info) {
  184. var domain = sequence.apply(null, info.args.map(argtype));
  185. var names = info.args.map(name);
  186. return label(info.name, transform(function(fieldVals) {
  187. return {id: info.id,
  188. fields: zipObject(fieldVals, names)};
  189. }, domain));
  190. }
  191. function properties(info) {
  192. var types = info.args.map(argtype);
  193. types.unshift(ULongLong); // size
  194. var domain = sequence.apply(null, types);
  195. var names = info.args.map(name);
  196. return label(info.name, transform(function(fieldVals) {
  197. return {id: info.id,
  198. size: fieldVals[0],
  199. fields: zipObject(fieldVals.slice(1), names)};
  200. }, domain));
  201. }
  202. var methods = [];
  203. var propertieses = [];
  204. for (var k in defs) {
  205. if (k.substr(0, 10) === 'methodInfo') {
  206. methods.push(method(defs[k]));
  207. methods[defs[k].name] = method(defs[k]);
  208. }
  209. else if (k.substr(0, 14) === 'propertiesInfo') {
  210. propertieses.push(properties(defs[k]));
  211. propertieses[defs[k].name] = properties(defs[k]);
  212. }
  213. };
  214. module.exports = {
  215. Octet: Octet,
  216. ShortStr: ShortStr,
  217. LongStr: LongStr,
  218. UShort: UShort,
  219. ULong: ULong,
  220. ULongLong: ULongLong,
  221. Short: Short,
  222. Long: Long,
  223. LongLong: LongLong,
  224. Bit: Bit,
  225. Double: Double,
  226. Float: Float,
  227. Timestamp: Timestamp,
  228. Decimal: Decimal,
  229. UnsignedByte: UnsignedByte,
  230. UnsignedShort: UnsignedShort,
  231. UnsignedInt: UnsignedInt,
  232. FieldArray: FieldArray,
  233. FieldTable: FieldTable,
  234. methods: methods,
  235. properties: propertieses
  236. };
  237. module.exports.rangeInt = rangeInt;