marshal
Version:
Parse Ruby's Marshal strings into JavaScript objects/JSON.
306 lines (289 loc) • 14.6 kB
JavaScript
var test = require('tape');
var Marshal = require('../index.js');
// NOTE: Marshal.dump(<var>).unpack('H*') is invaluable for creating these buffer strings in hex
var version = Buffer.from('0408', 'hex');
var nily = Buffer.from('040830', 'hex');
var truey = Buffer.from('040854', 'hex');
var falsey = Buffer.from('040846', 'hex');
var intyZero = Buffer.from('04086900', 'hex');
var intyOne = Buffer.from('04086906', 'hex');
var intyOneTwoThree = Buffer.from('040869017b', 'hex');
var intyOneFiveZero = Buffer.from('0408690196', 'hex');
var intyTwoFiveSix = Buffer.from('040869020001', 'hex');
var intySixFiveFiveThreeSix = Buffer.from('04086903000001', 'hex');
var intyTwoPowerThirtyMinusOne = Buffer.from('04086904ffffff3f', 'hex');
var intyNegOne = Buffer.from('040869fa', 'hex');
var intyNegOneTwoFour = Buffer.from('040869ff84', 'hex');
var intyNegOneTwoNine = Buffer.from('040869ff7f', 'hex');
var intyNegOneFiveZero = Buffer.from('040869ff6a', 'hex');
var intyNegTwoFiveSeven = Buffer.from('040869fefffe', 'hex');
var intyNegTwoFiveSix = Buffer.from('040869ff00', 'hex');
var intyNegSixFiveFiveThreeSeven = Buffer.from('040869fdfffffe', 'hex');
var intyNegTwoPowerThirty = Buffer.from('040869fc000000c0', 'hex');
var bigIntyTwoPowerThirty = Buffer.from('04086c2b0700000040', 'hex'); // Ruby smallest positive bignum
var bigIntyTwoPowerFiftyThreeMinusOne = Buffer.from('04086c2b09ffffffffffff1f00', 'hex'); // JS Number max safe integer
var bigIntyTwoPowerSixtyFour = Buffer.from('\u0004\bl+\n\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0000', 'utf8'); // https://github.com/ruby/spec/blob/a8afb240adcb413994d4de4d33f3f725105811da/core/marshal/fixtures/marshal_data.rb#L360
var bigIntyTwoPowerNinety = Buffer.from('\u0004\bl+\v\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0004', 'utf8'); // https://github.com/ruby/spec/blob/a8afb240adcb413994d4de4d33f3f725105811da/core/marshal/fixtures/marshal_data.rb#L362
var bigIntyNegTwoPowerThirtyMinusOne = Buffer.from('04086c2d0701000040', 'hex'); // Ruby smallest negative bignum
var bigIntyNegTwoPowerFiftyThreeMinusOne = Buffer.from('04086c2d09ffffffffffff1f00', 'hex'); // JS Number min safe integer
var bigIntyNegTwoPowerSixtyFour = Buffer.from('\u0004\bl-\n\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0000', 'utf8'); // https://github.com/ruby/spec/blob/a8afb240adcb413994d4de4d33f3f725105811da/core/marshal/fixtures/marshal_data.rb#L338
var symbolyHello = Buffer.from('04083a0a68656c6c6f', 'hex');
var symbolLinkyHello = Buffer.from('04085b073a0a68656c6c6f3b00', 'hex');
var objectLinkyHello = Buffer.from('04085b0749220a68656c6c6f063a0645544006', 'hex');
var objectNameIsLink = Buffer.from('04085b085b263a0773313a0773323a0773333a0773343a0773353a0773363a0773373a0773383a0773393a087331303a087331313a087331323a087331333a087331343a087331353a087331363a087331373a087331383a087331393a087332303a087332313a087332323a087332333a087332343a087332353a087332363a087332373a087332383a087332393a087333303a087333313a087333323a087333336f3a0642006f3b2600', 'hex'); // #6
var stringyEmpty = Buffer.from('04082200', 'hex');
var stringyHello = Buffer.from('0408220a68656c6c6f', 'hex');
var ivaryHelloUtf = Buffer.from('040849220a68656c6c6f063a064554', 'hex');
var ivaryHelloAscii = Buffer.from('040849220a68656c6c6f063a064546', 'hex');
var ivaryHelloIso8859Dash1 = Buffer.from('040849220a68656c6c6f063a0d656e636f64696e67220f49534f2d383835392d31', 'hex');
var ivaryLorem = Buffer.from('04082201f64c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e7365637465747572206164697069736963696e6720656c69742e20526570656c6c617420766f6c757074617320726572756d20656f7320656c6967656e64692c20647563696d7573206c61626f72756d206578706c696361626f2074656d706f72696275732076656c206970736120657865726369746174696f6e656d2070726f766964656e74206f62636165636174692065756d206c61626f72652065787065646974612061747175652c20646f6c6f72656d717565206c61626f72696f73616d206e6973692c20726570726568656e64657269742e063a064554', 'hex');
var arrayyEmpty = Buffer.from('04085b00', 'hex');
var arrayyInteger = Buffer.from('04085b066906', 'hex');
var objectyEmpty = Buffer.from('04086f3a0b4f626a65637400', 'hex');
var objectyFoo = Buffer.from('04086f3a0b4f626a656374063a0940666f6f492208626172063a064554', 'hex');
var hashyEmpty = Buffer.from('04087b00', 'hex');
var hashyOneTwo = Buffer.from('04087b0669066907', 'hex');
var railsSessionCookie = Buffer.from('04087b0b49220f73657373696f6e5f6964063a0645544922253836663666666139323537363739653231633733306236376138626263363233063b00544922105f637372665f746f6b656e063b004649223175557364487970586e6576346a6a4932646f554271317049795270637a5959536c443968354c78445a51553d063b004649220c757365725f6964063b004649220196636f6e6e6563742e736573733d732533416a2533412537422532327573657225323225334125323230313631626663652d616665302d346239652d386265382d6530323637653263646465322532322537442e4c4664327a652532465973316750744979456f597257665861747464633144774a353469576c332532462532426a6b35553b20506174683d2f3b20487474704f6e6c79063b005449221270617373776f72645f68617368063b00464922253334666263633762663264306265613335356164373938653032383938663563063b00464922106c6f636174696f6e5f6964063b004649222932356239306561632d303736392d343864382d393866612d383861343863633830376138063b005449220f6163636f756e745f6964063b004649222939363661656139662d636266302d346137392d383061642d623131633966393565626130063b0054', 'hex');
var floatyPositive = Buffer.from('0408660c31322e33343536', 'hex');
var floatyNegative = Buffer.from('0408660d2d31322e33343536', 'hex');
var floatyPositiveInfinity = Buffer.from('04086608696e66', 'hex');
var floatyNegativeInfinity = Buffer.from('040866092d696e66', 'hex');
var floatyNaN = Buffer.from('040866086e616e', 'hex');
test('marshal', function (t) {
t.test('parse', function (t) {
var m = new Marshal();
t.test('version', function (t) {
t.equal(m.load(version)._version, '4.8', 'should equal 4.8');
t.end();
});
t.test('nil', function (t) {
t.equal(m.load(nily).parsed, null, 'should equal null');
t.end();
});
t.test('booleans', function (t) {
t.test('true', function (t) {
t.equal(m.load(truey).parsed, true, 'should equal true');
t.end();
});
t.test('false', function (t) {
t.equal(m.load(falsey).parsed, false, 'should equal false');
t.end();
});
t.end();
});
t.test('integers', function (t) {
t.test('0', function (t) {
t.equal(m.load(intyZero).parsed, 0, 'should equal 0');
t.end();
});
t.test('1', function (t) {
t.equal(m.load(intyOne).parsed, 1, 'should equal 1');
t.end();
});
t.test('123', function (t) {
t.equal(m.load(intyOneTwoThree).parsed, 123, 'should equal 123');
t.end();
});
t.test('150', function (t) {
t.equal(m.load(intyOneFiveZero).parsed, 150, 'should equal 150');
t.end();
});
t.test('256', function (t) {
t.equal(m.load(intyTwoFiveSix).parsed, 256, 'should equal 256');
t.end();
});
t.test('65536', function (t) {
t.equal(m.load(intySixFiveFiveThreeSix).parsed, 65536, 'should equal 65536');
t.end();
});
t.test('2^30 - 1', function (t) {
t.equal(m.load(intyTwoPowerThirtyMinusOne).parsed, Math.pow(2, 30) - 1, 'should equal 2^30 - 1');
t.end();
});
t.test('-1', function (t) {
t.equal(m.load(intyNegOne).parsed, -1, 'should equal -1');
t.end();
});
t.test('-124', function (t) {
t.equal(m.load(intyNegOneTwoFour).parsed, -124, 'should equal -124');
t.end();
});
t.test('-129', function (t) {
t.equals(m.load(intyNegOneTwoNine).parsed, -129, 'should equal -129');
t.end();
});
t.test('-150', function (t) {
t.equals(m.load(intyNegOneFiveZero).parsed, -150, 'should equal -150');
t.end();
});
t.test('-256', function (t) {
t.equals(m.load(intyNegTwoFiveSix).parsed, -256, 'should equal -256');
t.end();
});
t.test('-257', function (t) {
t.equal(m.load(intyNegTwoFiveSeven).parsed, -257, 'should equal -257');
t.end();
});
t.test('-65537', function (t) {
t.equal(m.load(intyNegSixFiveFiveThreeSeven).parsed, -65537, 'should equal -65537');
t.end();
});
t.test('-2^30', function (t) {
t.equal(m.load(intyNegTwoPowerThirty).parsed, -Math.pow(2, 30), 'should equal -2^30');
t.end();
});
t.end();
});
t.test('bignums', function (t) {
t.test('2^90', function (t) {
t.equal(m.load(bigIntyTwoPowerNinety).parsed, '1237940039285380274899124224', 'should equal "1237940039285380274899124224"');
t.end();
});
t.test('2^64', function (t) {
t.equal(m.load(bigIntyTwoPowerSixtyFour).parsed, '18446744073709551616', 'should equal "18446744073709551616"');
t.end();
});
t.test('2^30', function (t) {
t.equal(m.load(bigIntyTwoPowerThirty).parsed, '1073741824', 'should equal "1073741824"');
t.end();
});
t.test('2^53 - 1', function (t) {
t.equal(m.load(bigIntyTwoPowerFiftyThreeMinusOne).parsed, '9007199254740991', 'should equal "9007199254740991"');
t.end();
});
t.test('-2^30 - 1', function (t) {
t.equal(m.load(bigIntyNegTwoPowerThirtyMinusOne).parsed, '-1073741825', 'should equal "-1073741825"');
t.end();
});
t.test('-2^53 - 1', function (t) {
t.equal(m.load(bigIntyNegTwoPowerFiftyThreeMinusOne).parsed, '-9007199254740991', 'should equal "-9007199254740991"');
t.end();
});
t.test('-2^64', function (t) {
t.equal(m.load(bigIntyNegTwoPowerSixtyFour).parsed, '-18446744073709551616', 'should equal "-18446744073709551616"');
t.end();
});
});
t.test('strings', function (t) {
t.test('<empty>', function (t) {
t.equal(m.load(stringyEmpty).parsed, '', 'should equal ""');
t.end();
});
t.test('hello', function (t) {
t.equal(m.load(stringyHello).parsed, 'hello', 'should equal "hello"');
t.end();
});
t.end();
});
t.test('symbols', function (t) {
t.test(':hello', function (t) {
t.equal(m.load(symbolyHello).parsed, 'hello', 'should equal "hello"');
t.end();
});
t.end();
});
t.test('symbol links', function (t) {
t.test(';0', function (t) {
t.equal(m.load(symbolLinkyHello).parsed[1], 'hello', 'should equal "hello"');
t.end();
});
t.end();
});
t.test('object links', function (t) {
t.test('@0', function (t) {
t.equal('' + m.load(objectLinkyHello).parsed[1], 'hello', 'should equal "hello"');
t.end();
});
t.end();
});
t.test('ivars', function (t) {
t.test('hello (utf8)', function (t) {
t.equal('' + m.load(ivaryHelloUtf).parsed, 'hello', 'should equal "hello"');
t.end();
});
t.test('hello (ascii)', function (t) {
t.equal('' + m.load(ivaryHelloAscii).parsed, 'hello', 'should equal "hello"');
t.end();
});
t.test('hello (ISO-8859-1)', function (t) {
t.equal('' + m.load(ivaryHelloIso8859Dash1).parsed, 'hello', 'should equal "hello"');
t.end();
});
t.test('lorem', function (t) {
t.equal('' + m.load(ivaryLorem).parsed, 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repellat voluptas rerum eos eligendi, ducimus laborum explicabo temporibus vel ipsa exercitationem provident obcaecati eum labore expedita atque, doloremque laboriosam nisi, reprehenderit.', 'should be "Lorem ipsum ..."');
t.end();
});
t.end();
});
t.test('arrays', function (t) {
t.test('[]', function (t) {
var array = m.load(arrayyEmpty).parsed;
t.ok(array instanceof Array, 'should be an array instance');
t.equal(array.length, 0, 'should be empty');
t.end();
});
t.test('[1]', function (t) {
var array = m.load(arrayyInteger).parsed;
t.ok(array instanceof Array, 'should be an array instance');
t.equal(array.length, 1, 'should have one member');
t.end();
});
t.end();
});
t.test('objects', function (t) {
t.test('empty', function (t) {
t.deepEqual(m.load(objectyEmpty).parsed, { _name: 'Object' }, 'should equal { _name: \'Object\' }');
t.end();
});
t.test('single instance variable \'@foo\'', function (t) {
t.deepEqual(m.load(objectyFoo).parsed, { _name: 'Object', '@foo': 'bar' }, 'should equal { _name: \'Object\', \'@foo\': \'bar\' }');
t.end();
});
t.test('name is link', function (t) {
t.deepEqual(m.load(objectNameIsLink).parsed, [
['s1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11', 's12', 's13', 's14', 's15', 's16', 's17', 's18', 's19', 's20', 's21', 's22', 's23', 's24', 's25', 's26', 's27', 's28', 's29', 's30', 's31', 's32', 's33'],
{ _name: 'B' },
{ _name: 'B' }
], 'should equal the test object with a name that is a symbol link and a high link index (see #6)');
t.end();
});
t.end();
});
t.test('hashes', function (t) {
t.test('{}', function (t) {
t.deepEqual(m.load(hashyEmpty).parsed, {}, 'should equal {}');
t.end();
});
t.test('{ 1: 2 }', function (t) {
t.deepEqual(m.load(hashyOneTwo).parsed, { 1: 2 }, 'should equal { 1: 2 }');
t.end();
});
t.end();
});
t.test('floats', function (t) {
t.test('positive float', function (t) {
t.equal(m.load(floatyPositive).parsed, 12.3456, 'should equal 12.3456');
t.end();
});
t.test('negative float', function (t) {
t.equal(m.load(floatyNegative).parsed, -12.3456, 'should equal -12.3456');
t.end();
});
t.test('positive infinity', function (t) {
t.equal(m.load(floatyPositiveInfinity).parsed, Infinity, 'should equal Infinity');
t.end();
});
t.test('negative infinity', function (t) {
t.equal(m.load(floatyNegativeInfinity).parsed, -Infinity, 'should equal -Infinity');
t.end();
});
t.test('NaN', function (t) {
t.equal(Number.isNaN(m.load(floatyNaN).parsed), true, 'should equal NaN');
t.end();
});
t.end();
});
t.end();
});
t.end();
});