text:
(function graphicsTests() {
  var Shape = flash.display.Shape;
  var Graphics = flash.display.Graphics;
  var Matrix = flash.geom.Matrix;
  var GradientType = flash.display.GradientType;
  var SpreadMethod = flash.display.SpreadMethod;
  var InterpolationMethod = flash.display.InterpolationMethod;
  var BitmapData = flash.display.BitmapData;
  // We don't import Shumway.PathCommand because we don't want the TSC compiler
  // to preserve enum names. So we'll have to keep this in sync.
  var PathCommand = {
      BeginSolidFill: 1,
      BeginGradientFill: 2,
      BeginBitmapFill: 3,
      EndFill: 4,
      LineStyleSolid: 5,
      LineStyleGradient: 6,
      LineStyleBitmap: 7,
      LineEnd: 8,
      MoveTo: 9,
      LineTo: 10,
      CurveTo: 11,
      CubicCurveTo: 12
  };
  var assertUnreachable = Shumway.Debug.assertUnreachable;

  var LineScaleMode = flash.display.LineScaleMode;
  var CapsStyle = flash.display.CapsStyle;
  var JointStyle = flash.display.JointStyle;

  unitTests.push(basics);
  unitTests.push(clear);
  unitTests.push(beginFill);
  unitTests.push(beginGradientFill);
  unitTests.push(beginBitmapFill);
  unitTests.push(lineStyle_defaults);
  unitTests.push(lineStyle_invalidWidth);
  unitTests.push(lineStyle_allArgs);
  unitTests.push(lineGradientStyle);
  unitTests.push(lineBitmapStyle);
  unitTests.push(moveTo);
  unitTests.push(lineTo);
  unitTests.push(curveTo);
  unitTests.push(cubicCurveTo);
  unitTests.push(drawRect);
  unitTests.push(bounds);

  function basics() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    eq(shape.commandsPosition, 0, "Graphics instances start out empty");
    eq(shape.coordinatesPosition, 0, "Graphics instances start out empty");
    eq(shape.styles.length, 0, "Graphics instances start out empty");
    structEq(g._getContentBounds(),
             {xMin: 0x8000000, xMax: 0x8000000, yMin: 0x8000000, yMax: 0x8000000},
             "graphics instances initially have sentinel bounds");
  }

  function clear() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    g.lineStyle(1);
    neq(shape.commandsPosition, 0, "lineStyle writes command");
    neq(shape.styles.length, 0, "lineStyle writes style data");
    g.lineTo(100, 100);
    neq(shape.coordinatesPosition, 0, "lineTo writes coordinates");

    g.clear();
    eq(shape.commandsPosition, 0, "clear empties the instance's data");
    eq(shape.coordinatesPosition, 0, "clear empties the instance's data");
    eq(shape.styles.length, 0, "clear empties the instance's data");
    structEq(g._getContentBounds(),
             {xMin: 0x8000000, xMax: 0x8000000, yMin: 0x8000000, yMax: 0x8000000},
             "clear resets bounds");
  }

  function beginFill() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    g.beginFill(0xaabbcc);
    shape.styles.position = 0;
    eq(shape.commands[0], PathCommand.BeginSolidFill, "fill is stored");
    eq(shape.coordinatesPosition, 0, "fills don't write coordinates");
    eq(shape.styles.readUnsignedInt(), 0xaabbccff,
       "beginFill stores given color and default alpha");
    g.clear();

    g.beginFill(0xaabbcc, 0.5);
    shape.styles.position = 0;
    eq(shape.styles.readUnsignedInt(), 0xaabbcc80, "alpha is stored correctly");
    eq(shape.commandsPosition, 1, "instructions didn't write more data than expected");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
  }

  function beginGradientFill() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    // REDUX: enable this test once throwError works without AS3 on the stack.
//    assertThrowsInstanceOf(function() {g.beginGradientFill(null)}, TypeError,
//                           'beginGradientFill must specify a valid type');
    var matrix = new Matrix(1, 2, 3, 4, 5, 6);
    var colors = sec.createArrayUnsafe([0, 0xff0000]);
    var alphas = sec.createArrayUnsafe([1, 0.5]);
    var ratios = sec.createArrayUnsafe([45.3, 198.24]);
    g.beginGradientFill("linear", colors, alphas, ratios, matrix, "repeat", "linearRGB", -0.73);
    shape.styles.position = 0;
    eq(shape.commands[0], PathCommand.BeginGradientFill, "fill is stored");
    eq(shape.coordinatesPosition, 0, "fills don't write coordinates");
    eq(GradientType.axClass.fromNumber(shape.styles.readUnsignedByte()), "linear",
       'gradient type is stored');
    eq(shape.styles.readShort(), -93, 'focal point is stored as fixed8');
    matrix.toSerializedScaleInPlace();
    var deserializedMatrix = Matrix.axClass.FromDataBuffer(shape.styles);
    // REDUX: reenable once rounding differences have been figured out.
    //matrixEq(deserializedMatrix, matrix, "matrix is stored");

    eq(shape.styles.readUnsignedByte(), 2, 'number of color stops is stored');
    eq(shape.styles.readUnsignedByte(), 45, 'first ratio is stored');
    eq(shape.styles.readUnsignedInt(), 0x000000ff, 'first RGBA color is stored');
    eq(shape.styles.readUnsignedByte(), 198, 'second ratio is stored');
    eq(shape.styles.readUnsignedInt(), 0xff00007f, 'second RGBA color is stored');

    eq(SpreadMethod.axClass.fromNumber(shape.styles.readUnsignedByte()), "repeat",
       'spread method is stored');
    eq(InterpolationMethod.axClass.fromNumber(shape.styles.readUnsignedByte()),
       "linearRGB", 'interpolation method is stored');
    eq(shape.commandsPosition, 1, "instructions didn't write more data than expected");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
    g.clear();

    alphas.value = [1];
    ratios.value = [0, 100];
    g.beginGradientFill("linear", colors, alphas, ratios);
    eq(shape.commandsPosition, 0, "Calls of beginGradientFill with different " +
                                  "lengths for the colors, alphas and ratios " +
                                  "are ignored");
    eq(shape.coordinatesPosition, 0, "fills don't write coordinates");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
  }

  function beginBitmapFill() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    var bitmap = new BitmapData(100, 100);
    g.beginBitmapFill(bitmap);
    shape.styles.position = 0;
    eq(shape.commands[0], PathCommand.BeginBitmapFill, "fill is stored");
    eq(shape.coordinatesPosition, 0, "fills don't write coordinates");
    var index = shape.styles.readUnsignedInt();
    eq(index, 0, "beginBitmapFill stores given bitmap's id");
    eq(g.getUsedTextures()[index], bitmap, "beginBitmapFill stores given bitmap's id");
    matrixEq(Matrix.axClass.FromDataBuffer(shape.styles), Matrix.axClass.FROZEN_IDENTITY_MATRIX,
             "default matrix is serialized if none is provided");
    eq(shape.styles.readBoolean(), true, "defaults to repeat");
    eq(shape.styles.readBoolean(), false, "defaults to no smooting");
    eq(shape.commandsPosition, 1, "instructions didn't write more data than expected");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
    g.clear();

    try {
      g.beginBitmapFill(sec.createObject());
      assertUnreachable("beginBitmapFill with invalid bitmap argument throws");
    } catch (e) {
    }
    try {
      g.beginBitmapFill(bitmap, sec.createObject());
      assertUnreachable("beginBitmapFill with non-matrix 2nd argument throws");
    } catch (e) {
    }
    eq(shape.commandsPosition, 0, "invalid beginBitmapFill calls don't write a command");
    eq(shape.styles.length, 0, "invalid beginBitmapFill calls don't write style data");

    var matrix = new Matrix(1, 2, 3, 4, 5, 6);
    g.beginBitmapFill(bitmap, matrix, false, true);
    shape.styles.position = 0;
    shape.styles.readUnsignedInt(); // skip bitmap id
    matrixEq(Matrix.axClass.FromDataBuffer(shape.styles), matrix,
             "serialized matrix is identical to input matrix");
    eq(shape.styles.readBoolean(), false, "repeat flag is written correctly");
    eq(shape.styles.readBoolean(), true, "smooth flag is written correctly");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
  }

  function lineStyle_defaults() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    g.lineStyle(1);
    shape.styles.position = 0;
    eq(shape.commands[0], PathCommand.LineStyleSolid, "style is stored");
    eq(shape.coordinatesPosition, 1, "lineStyle writes thickness into coordinates");
    eq(shape.coordinates[0], 20, "given thickness is stored");
    eq(shape.styles.readUnsignedInt(), 0xff, "default color is full-opacity black");
    eq(shape.styles.readBoolean(), false, "defaults to no pixel hinting");
    eq(LineScaleMode.axClass.fromNumber(shape.styles.readUnsignedByte()), 'normal',
       "defaults to normal scaling");
    eq(CapsStyle.axClass.fromNumber(shape.styles.readUnsignedByte()), 'round',
       "defaults to round caps");
    eq(JointStyle.axClass.fromNumber(shape.styles.readUnsignedByte()), 'round',
       "defaults to round joints");
    eq(shape.styles.readUnsignedByte(), 3, "defaults to miterLimit of 3");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
    g.clear();

    g.lineStyle(0.4);
    shape.styles.position = 0;
    eq(shape.coordinates[0], 8, "thickness is correctly rounded");
    g.clear();

    g.lineStyle(0.6);
    shape.styles.position = 0;
    eq(shape.coordinates[0], 12, "thickness is correctly rounded");
    g.clear();

    g.lineStyle(1.1);
    shape.styles.position = 0;
    eq(shape.coordinates[0], 22, "thickness is correctly rounded");
    g.clear();

    g.lineStyle(1.5);
    shape.styles.position = 0;
    eq(shape.coordinates[0], 30, "thickness is correctly rounded");
  }

  function lineStyle_invalidWidth() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    g.lineStyle(NaN);
    eq(shape.commands[0], PathCommand.LineEnd, "style is stored");
    eq(shape.coordinatesPosition, 0, "styles don't write coordinates");
    eq(shape.commandsPosition, 1, "instructions didn't write more data than expected");
    eq(shape.styles.bytesAvailable, 0, "LineEnd doesn't write style data");
  }

  function lineStyle_allArgs() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    g.lineStyle(10, 0xaabbcc, 0.5, true, 'horizontal', 'square', 'bevel', 10);
    shape.styles.position = 0;
    eq(shape.commands[0], PathCommand.LineStyleSolid, "style is stored");
    eq(shape.coordinatesPosition, 1, "lineStyle writes thickness into coordinates");
    eq(shape.coordinates[0], 200, "given thickness is stored");
    eq(shape.styles.readUnsignedInt(), 0xaabbcc80, "color and alpha are stored correctly");
    eq(shape.styles.readBoolean(), true, "pixel hinting is stored");
    eq(LineScaleMode.axClass.fromNumber(shape.styles.readUnsignedByte()), 'horizontal',
       "lineScaleMode is stored");
    eq(CapsStyle.axClass.fromNumber(shape.styles.readUnsignedByte()), 'square',
       "capsStyle is stored");
    eq(JointStyle.axClass.fromNumber(shape.styles.readUnsignedByte()), 'bevel',
       "jointsStyle is stored");
    eq(shape.styles.readUnsignedByte(), 10, "miterLimit is stored");
    eq(shape.commandsPosition, 1, "instructions didn't write more data than expected");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
  }

  function lineGradientStyle() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    // TODO: enable this test once throwError works without AS3 on the stack.
//    assertThrowsInstanceOf(function() {g.beginGradientFill(null)}, TypeError,
//                           'beginGradientFill must specify a valid type');

    var colors = sec.createArrayUnsafe([0, 0xff0000]);
    var alphas = sec.createArrayUnsafe([1, 0.5]);
    var ratios = sec.createArrayUnsafe([45.3, 198.24]);
    g.lineGradientStyle("linear", colors, alphas, ratios);
    shape.styles.position = 0;
    eq(shape.commandsPosition, 0, "lineGradientStyle doesn't write data if no lineStyle is set");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
    g.clear();

    g.lineStyle(10, 0xff00ff);
    var initialPosition = shape.styles.position;
    var matrix = new Matrix(1, 2, 3, 4, 5, 6);
    g.lineGradientStyle("linear", colors, alphas, ratios, matrix, "repeat", "linearRGB", -0.73);
    shape.styles.position = initialPosition;
    eq(shape.commands[0], PathCommand.LineStyleSolid, "initial line style is stored");
    eq(shape.commands[1], PathCommand.LineStyleGradient, "gradient line style is stored");
    eq(shape.coordinatesPosition, 1, "lineStyle writes thickness into coordinates");
    eq(GradientType.axClass.fromNumber(shape.styles.readUnsignedByte()), "linear",
       'gradient type is stored');
    eq(shape.styles.readShort(), -93, 'focal point is stored as fixed8');
    matrix.toSerializedScaleInPlace();
    var deserializedMatrix = Matrix.axClass.FromDataBuffer(shape.styles);
    // REDUX: reenable once rounding differences have been figured out.
    //matrixEq(deserializedMatrix, matrix, "matrix is stored");

    eq(shape.styles.readUnsignedByte(), 2, 'number of color stops is stored');
    eq(shape.styles.readUnsignedByte(), 45, 'first ratio is stored');
    eq(shape.styles.readUnsignedInt(), 0x000000ff, 'first RGBA color is stored');
    eq(shape.styles.readUnsignedByte(), 198, 'second ratio is stored');
    eq(shape.styles.readUnsignedInt(), 0xff00007f, 'second RGBA color is stored');

    eq(SpreadMethod.axClass.fromNumber(shape.styles.readUnsignedByte()), "repeat",
       'spread method is stored');
    eq(InterpolationMethod.axClass.fromNumber(shape.styles.readUnsignedByte()),
       "linearRGB", 'interpolation method is stored');
    eq(shape.commandsPosition, 2, "instructions didn't write more data than expected");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
    g.clear();

    alphas.value = [1];
    ratios.value = [0, 100];
    g.lineGradientStyle("linear", colors, alphas, ratios);
    eq(shape.commandsPosition, 0, "Calls of lineGradientStyle with different lengths for the " +
                                  "colors, alphas and ratios are ignored");
    eq(shape.coordinatesPosition, 0, "fills don't write coordinates");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
  }

  function lineBitmapStyle() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    // TODO: enable this test once throwError works without AS3 on the stack.
//    assertThrowsInstanceOf(function() {g.beginGradientFill(null)}, TypeError,
//                           'beginGradientFill must specify a valid type');

    var bitmap = new BitmapData(100, 100);
    g.lineBitmapStyle(bitmap);
    shape.styles.position = 0;
    eq(shape.commandsPosition, 0, "lineBitmapStyle doesn't write data if no lineStyle is set");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
    g.clear();

    g.lineStyle(10, 0xff00ff);
    var initialPosition = shape.styles.position;
    g.lineBitmapStyle(bitmap);
    shape.styles.position = initialPosition;
    eq(shape.commands[1], PathCommand.LineStyleBitmap, "style is stored");
    eq(shape.coordinatesPosition, 1, "bitmap styles don't write coordinates");
    var index = shape.styles.readUnsignedInt();
    eq(index, 0, "lineBitmapStyle stores given bitmap's id");
    eq(g.getUsedTextures()[index], bitmap, "lineBitmapStyle stores given bitmap's id");
    matrixEq(Matrix.axClass.FromDataBuffer(shape.styles), Matrix.axClass.FROZEN_IDENTITY_MATRIX,
             "default matrix is serialized if none is provided");
    eq(shape.styles.readBoolean(), true, "defaults to repeat");
    eq(shape.styles.readBoolean(), false, "defaults to no smooting");
    eq(shape.commandsPosition, 2, "instructions didn't write more data than expected");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
    g.clear();

    try {
      g.lineBitmapStyle(sec.createObject());
      assertUnreachable("lineBitmapStyle with invalid bitmap argument throws");
    } catch (e) {
    }
    try {
      g.lineBitmapStyle(bitmap, sec.createObject());
      assertUnreachable("lineBitmapStyle with non-matrix 2nd argument throws");
    } catch (e) {
    }
    eq(shape.commandsPosition, 0, "invalid lineBitmapStyle calls don't write a command");
    eq(shape.styles.length, 0, "invalid lineBitmapStyle calls don't write style data");

    g.lineStyle(10, 0xff00ff);
    var matrix = new Matrix(1, 2, 3, 4, 5, 6);
    initialPosition = shape.styles.position;
    g.lineBitmapStyle(bitmap, matrix, false, true);
    shape.styles.position = initialPosition;
    shape.styles.readUnsignedInt(); // skip bitmap id
    matrixEq(Matrix.axClass.FromDataBuffer(shape.styles), matrix,
             "serialized matrix is identical to input matrix");
    eq(shape.styles.readBoolean(), false, "repeat flag is written correctly");
    eq(shape.styles.readBoolean(), true, "smooth flag is written correctly");
    eq(shape.styles.bytesAvailable, 0, "instructions didn't write more data than expected");
  }

  function moveTo() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    g.moveTo(100, 50);
    shape.styles.position = 0;
    eq(shape.styles.length, 0, "path commands don't write style data");
    eq(shape.commands[0], PathCommand.MoveTo, "command is stored");
    eq(shape.coordinates[0], 100 * 20, "x is stored correctly");
    eq(shape.coordinates[1], 50 * 20, "y is stored correctly");
    eq(shape.commandsPosition, 1, "instructions didn't write more data than expected");
    eq(shape.coordinatesPosition, 2, "instructions didn't write more data than expected");
  }

  function lineTo() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    g.lineTo(100, 50);
    eq(shape.styles.length, 0, "path commands don't write style data");
    eq(shape.commands[0], PathCommand.LineTo, "command is stored");
    eq(shape.coordinates[0], 100 * 20, "x is stored correctly");
    eq(shape.coordinates[1], 50 * 20, "y is stored correctly");
    eq(shape.commandsPosition, 1, "instructions didn't write more data than expected");
    eq(shape.coordinatesPosition, 2, "instructions didn't write more data than expected");
  }

  function curveTo() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    g.curveTo(100, 50, 0, 100);
    eq(shape.commands[0], PathCommand.CurveTo, "command is stored");
    eq(shape.styles.length, 0, "path commands don't write style data");
    eq(shape.coordinates[0], 100 * 20, "cpx is stored correctly");
    eq(shape.coordinates[1], 50 * 20, "cpy is stored correctly");
    eq(shape.coordinates[2], 0 * 20, "x is stored correctly");
    eq(shape.coordinates[3], 100 * 20, "y is stored correctly");
    eq(shape.commandsPosition, 1, "instructions didn't write more data than expected");
    eq(shape.coordinatesPosition, 4, "instructions didn't write more data than expected");
  }

  function cubicCurveTo() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    g.cubicCurveTo(100, 50, -100, 100, 0, 150);
    eq(shape.styles.length, 0, "path commands don't write style data");
    eq(shape.commands[0], PathCommand.CubicCurveTo, "command is stored");
    eq(shape.coordinates[0], 100 * 20, "cpx1 is stored correctly");
    eq(shape.coordinates[1], 50 * 20, "cpy1 is stored correctly");
    eq(shape.coordinates[2], -100 * 20, "cpx2 is stored correctly");
    eq(shape.coordinates[3], 100 * 20, "cpy2 is stored correctly");
    eq(shape.coordinates[4], 0 * 20, "x is stored correctly");
    eq(shape.coordinates[5], 150 * 20, "y is stored correctly");
    eq(shape.commandsPosition, 1, "instructions didn't write more data than expected");
    eq(shape.coordinatesPosition, 6, "instructions didn't write more data than expected");
  }

  function drawRect() {
    var g = createGraphics();
    var shape = g.getGraphicsData();

    g.drawRect(0, 0, 100, 100);
    eq(shape.styles.length, 0, "path commands don't write style data");
    eq(shape.commands[0], PathCommand.LineTo, "1. lineTo command is stored");
    eq(shape.coordinates[0], 100 * 20, "x is stored correctly");
    eq(shape.coordinates[1], 0 * 20, "y is stored correctly");
    eq(shape.commands[0], PathCommand.LineTo, "2. lineTo command is stored");
    eq(shape.coordinates[2], 100 * 20, "x is stored correctly");
    eq(shape.coordinates[3], 100 * 20, "y is stored correctly");
    eq(shape.commands[0], PathCommand.LineTo, "3. lineTo command is stored");
    eq(shape.coordinates[4], 0 * 20, "x is stored correctly");
    eq(shape.coordinates[5], 100 * 20, "y is stored correctly");
    eq(shape.commands[0], PathCommand.LineTo, "4. lineTo command is stored");
    eq(shape.coordinates[6], 0 * 20, "x is stored correctly");
    eq(shape.coordinates[7], 0 * 20, "y is stored correctly");
    eq(shape.commandsPosition, 4, "instructions didn't write more data than expected");
    eq(shape.coordinatesPosition, 8, "instructions didn't write more data than expected");

    g.drawRect(200, 200, 100, 100);
    eq(shape.commands[4], PathCommand.MoveTo, "moveTo command is stored if rect doesn't " +
                                              "start at previous cursor coordinates");
    eq(shape.coordinates[8], 200 * 20, "x is stored correctly");
    eq(shape.coordinates[9], 200 * 20, "y is stored correctly");
    eq(shape.commandsPosition, 9, "instructions didn't write more data than expected");
    eq(shape.coordinatesPosition, 18, "instructions didn't write more data than expected");
  }

  // Note: these tests aren't really valid, but will do as a first approximation.
  // Note: amazingly, Flash happily extends bounds for drawing operations without strokes and fills.
  // The only exception are multiple moveTo operations.
  function bounds() {
    var g = createGraphics();

    structEq(g._getContentBounds(false),
             {xMin: 0x8000000, xMax: 0x8000000, yMin: 0x8000000, yMax: 0x8000000},
             "bounds are initialized with sentinel values.");
    g.moveTo(150, 50);
    structEq(g._getContentBounds(),
             {xMin: 0x8000000, xMax: 0x8000000, yMin: 0x8000000, yMax: 0x8000000},
             "empty move doesn't change bounds");

    g.clear();
    g.lineTo(100, 50);
    structEq(g._getContentBounds(), {xMin: 0, xMax: 2000, yMin: 0, yMax: 1000},
             "line extends bounds");

    g.clear();
    g.curveTo(100, 100, 0, 100);
    structEq(g._getContentBounds(), {xMin: 0, xMax: 1000, yMin: 0, yMax: 2000},
             "curve extends bounds correctly");
    g.clear();
    g.moveTo(30, 130);
    g.lineStyle(1);
    g.curveTo(0, 0, 130, 30);
    structEq(g._getContentBounds(false), {xMin: 487, xMax: 2600, yMin: 487, yMax: 2600},
             "curve extends fill bounds correctly");
    structEq(g._getContentBounds(true), {xMin: 477, xMax: 2610, yMin: 477, yMax: 2610},
             "curve extends line bounds correctly");

    g.clear();
    g.lineStyle(1);
    g.moveTo(30, 50);
    g.cubicCurveTo(60, -10, 180, 200, 150, 100);
    structEq(g._getContentBounds(false), {xMin: 600, xMax: 3095, yMin: 780, yMax: 2549},
             "cubic curve extends fill bounds correctly");
    structEq(g._getContentBounds(true), {xMin: 590, xMax: 3105, yMin: 770, yMax: 2559},
             "cubic curve extends line bounds correctly");

    g.clear();
    g.lineStyle(1);
    g.cubicCurveTo(100, 50, -100, 100, 0, 150);
    structEq(g._getContentBounds(false), {xMin: -577, xMax: 577, yMin: 0, yMax: 3000},
             "cubic curve extends fill bounds correctly");
    structEq(g._getContentBounds(true), {xMin: -587, xMax: 587, yMin: -10, yMax: 3010},
             "cubic curve extends line bounds correctly");
  }

  function createGraphics() {
    return new Shape().graphics;
  }

})();


text:
$(document).ready(function() {
	get_tasks();
	checkbox();
});

$("#form_data").on('submit',(function(e) {
	jQuery( '.progess_div' ).html('
 Saving
');
	e.preventDefault();
	$.ajax({
		url: base_url+"ajax/insert_data",
		type: "POST",
		data:  new FormData(this),
		contentType: false,
		cache: false,
		processData:false,
		dataType: "json",
		success: function(data)
		{
			if(data.status == 'ok'){
				setTimeout(
					function()
					{
						jQuery( '.oper_status' ).html('Saved');
						setTimeout(
							function()
							{
								jQuery('.close_model').click();
								get_tasks();
								checkbox();
								jQuery('.form-control').val('');
								jQuery('#item_id').val(0);
								jQuery( '.progess_div' ).html('<button type="submit" id="insert_btn" class="btn btn-primary save_btn">Save</button>');
							}, 1000);
					}, 1000);
			}
		},
		error: function()
		{
		}
	});
}));

$("#sub_form_data").on('submit',(function(e) {
	jQuery( '.sub_progess_div' ).html('
 Saving
');
	e.preventDefault();
	$.ajax({
		url: base_url+"ajax/insert_sub_data",
		type: "POST",
		data:  new FormData(this),
		contentType: false,
		cache: false,
		processData:false,
		dataType: "json",
		success: function(data)
		{
			if(data.status == 'ok'){
				setTimeout(
					function()
					{
						jQuery( '.sub_oper_status' ).html('Saved');
						setTimeout(
							function()
							{
								jQuery('.close_model').click();
								jQuery('.form-control').val('');
								jQuery('#sub_item_id').val(0);
								jQuery( '.sub_progess_div' ).html('<button type="submit" id="sub_insert_btn" class="btn btn-primary save_btn">Save</button>');
								get_subs(jQuery('#sub_parent_id').val());
								checkbox();
							}, 1000);
					}, 1000);
			}
		},
		error: function()
		{
		}
	});
}));

function get_tasks(oper = 'none', filter = 3){
	jQuery('#main-tasks').html('

');
	jQuery.post(base_url+"ajax/get_tasks" ,{oper:oper , filter:filter}, function(data){
		if(data.status == 'ok'){
			jQuery('#main-tasks').html(data.data);
			checkbox();
			click_on_main();
			edit_main_click();
			done_action();
			remove_click();
			undo_sub_click();
			filterTasks();
			filterSubs();
			undo_click();
			if(data.is_next != 1){
				jQuery('.next_main').addClass('hidden-btn');
			}else{
				jQuery('.next_main').removeClass('hidden-btn');
			}

			if(data.is_pre != 1){
				jQuery('.pre_main').addClass('hidden-btn');
			}else{
				jQuery('.pre_main').removeClass('hidden-btn');
			}

			if(jQuery('#save_main').val() != 0){
				var task_id = jQuery('#save_main').val();
				jQuery("#"+task_id).addClass('active');
			}
		}
	},"json");

}

function checkbox(){
	$('input.icheck-red').iCheck({
		checkboxClass: 'icheckbox_minimal-blue',
		radioClass: 'iradio_minimal-blue',
        increaseArea: '20%' // optional
    });

	$('input.icheck-black').iCheck({
		checkboxClass: 'icheckbox_square-aero',
		radioClass: 'iradio_square-aero',
        increaseArea: '20%' // optional
    });
}

function click_on_main(){
	$('.task-li').each( function(){
		var item = this ;
		jQuery("#"+item.id).click(function(e){
			eventTarget = e.target;
			eventTargetClass = $(eventTarget).attr('class');
			if (eventTarget == this || eventTargetClass == "task-title" ) {
				jQuery('.task-li').removeClass('active');
				jQuery(item).addClass('active');
				jQuery('#save_main').val(item.id);
				get_subs( item.id );
			}
		});
	});
}

function get_subs(id,oper = 'none',filter = 3){
	jQuery('#sub-tasks').html('

');
	jQuery.post(base_url+"ajax/get_subs" ,{parent_id:id,oper:oper,filter:filter}, function(data){
		if(data.status == 'ok'){

			if(data.is_next != 1){
				jQuery('.next_sub').addClass('hidden-btn');
			}else{
				jQuery('.next_sub').removeClass('hidden-btn');
			}

			if(data.is_pre != 1){
				jQuery('.pre_sub').addClass('hidden-btn');
			}else{
				jQuery('.pre_sub').removeClass('hidden-btn');
			}

			jQuery('.sub-btn').removeClass('hidden-btn');
			if(data.parent_id != 0){
				jQuery('.back-btn').removeClass('hidden-btn');
				jQuery('#back_value').val(data.parent_id);
			}else{
				jQuery('.back-btn').addClass('hidden-btn');
			}
			jQuery('#sub_parent_id').val(id);
			jQuery('.sub_task_title').html(' '+data.parent_title);
			jQuery('#sub-tasks').html(data.data);
			jQuery('li#'+id+" .taskNums").html("("+data.count+")");
			checkbox();
			sub_done_action();
			click_on_sub();
			edit_sub_click();
			done_action();
			remove_sub_click();
			undo_sub_click();
			filterTasks();
			filterSubs();
		}
	},"json");
}

function click_on_sub(){
	$('.sub-task-li').each( function(){
		var item = this ;
		jQuery("#"+item.id).click(function(e){
			eventTarget = e.target;
			eventTargetClass = $(eventTarget).attr('class');
			if (eventTarget == this || eventTargetClass == "task-title" ) {
				jQuery('.sub-task-li').removeClass('active');
				jQuery(item).addClass('active');
				get_subs( item.id );
			}
		});
	});
}

jQuery(".back-btn").on('click',function(){
	get_subs(jQuery('#back_value').val());
});

function edit_main_click(){
	$('.edit_btn_main').each( function(){
		var item = this ;
		jQuery(item).click(function(){
			var task_id = jQuery(item).closest('li.task-li').attr('id');
			var task_title = jQuery('li#'+task_id+" .task-title").html();
			jQuery('#task_title').val(task_title);
			jQuery('#item_id').val(task_id);
			jQuery('.main-btn').click();
		});
	});
}

function edit_sub_click(){
	$('.edit_btn_sub').each( function(){
		var item = this ;
		jQuery(item).click(function(){
			var task_id = jQuery(item).closest('li.sub-task-li').attr('id');
			var task_title = jQuery('li#'+task_id+" .task-title").html();
			jQuery('#sub_task_title').val(task_title);
			jQuery('#sub_item_id').val(task_id);
			jQuery('.sub-btn').click();

		});
	});
}

function remove_click(){
	$('.remove_btn').each( function(){
		var item = this ;
		jQuery(item).click(function(){
			bootbox.confirm("Are you sure?", function(result) {
				if(result){
					var task_id = jQuery(item).closest('li.task-li').attr('id');
					jQuery.post(base_url+"ajax/remove_main_task" ,{task_id:task_id}, function(data){
						if(data.status == 'ok'){
							jQuery('li#'+task_id).hide(500);
							jQuery('#sub-tasks').html(data.message);
							jQuery('.back-btn').addClass('hidden-btn');
							jQuery('.sub-btn').addClass('hidden-btn');
							jQuery('.sub_task_title').html(' Dependencies Tasks ');
						}
					},"json");
				}
			});
		});
	});
}

function remove_sub_click(){
	$('.remove_btn_sub').each( function(){
		var item = this ;
		jQuery(item).click(function(){
			bootbox.confirm("Are you sure?", function(result) {
				if(result){
					var task_id = jQuery(item).closest('li.sub-task-li').attr('id');
					var main_task = jQuery('#save_main').val();
					jQuery.post(base_url+"ajax/remove_task" ,{task_id:task_id,main_task:main_task}, function(data){
						if(data.status == 'ok'){
							jQuery('li#'+task_id).hide(500);
							jQuery('li#'+main_task+" .status_div").html(data.main_status_result);
							jQuery('li#'+main_task+" .check_task").html(data.main_label);
						}
					},"json");
				}
			});
		});
	});
}

function undo_sub_click(){
	$('.undo_btn_sub').each( function(){
		var item = this ;
		jQuery(item).click(function(){
			var task_id = jQuery(item).closest('li.sub-task-li').attr('id');
			var main_task = jQuery('#save_main').val();
			jQuery.post(base_url+"ajax/undo_action" ,{task_id:task_id,main_task:main_task}, function(data){
				if(data.status == 'ok'){
					jQuery('li#'+task_id+" .status_div").html(data.status_label);
					jQuery('li#'+task_id+" .check_task").html(data.label);
					jQuery('li#'+main_task+" .status_div").html(data.main_status_result);
					jQuery('li#'+main_task+" .check_task").html(data.main_label);
					jQuery('#redoLi_'+task_id).addClass('hidden-btn');
					checkbox();
					sub_done_action();
					filterTasks();
					filterSubs();
				}
			},"json");
		});
	});
}

function undo_click(){
	$('.undo_btn').each( function(){
		var item = this ;
		jQuery(item).click(function(){
			var task_id = jQuery(item).closest('li.task-li').attr('id');
			jQuery.post(base_url+"ajax/undo_main_action" ,{task_id:task_id}, function(data){
				if(data.status == 'ok'){
					jQuery('li#'+task_id+" .status_div").html(data.status_label);
					jQuery('li#'+task_id+" .check_task").html(data.label);
					jQuery('#redoLi_'+task_id).addClass('hidden-btn');
					checkbox();
					done_action();
					filterTasks();
					filterSubs();
				}
			},"json");
		});
	});
}

$('#newTask').on('hidden.bs.modal', function () {
	jQuery('.form-control').val('');
	jQuery('#item_id').val(0);
})

$('#newSubTask').on('hidden.bs.modal', function () {
	jQuery('.form-control').val('');
	jQuery('#sub_item_id').val(0);
})

function done_action(){
	$('.done-btn').each( function(){
		var item = this ;
		$(item).on('ifChecked', function(event){
			var task_id = jQuery(item).closest('li').attr('id');
			var main_task = task_id;
			jQuery.post(base_url+"ajax/done_action" ,{task_id:task_id,main_task:main_task}, function(data){
				if(data.status == 'ok'){
					jQuery('li#'+task_id+" .status_div").html(data.status_label);
					jQuery('li#'+task_id+" .check_task").html(data.label);
					if(data.is_redo == 1){
						jQuery('#redoLi_'+task_id).removeClass('hidden-btn');
						undo_sub_click();
					}
					filterTasks();
					filterSubs();
				}
			},"json");
		});
	});
}

function sub_done_action(){
	$('.sub-done-btn').each( function(){
		var item = this ;
		$(item).on('ifChecked', function(event){
			var task_id = jQuery(item).closest('li').attr('id');
			var main_task = jQuery('#save_main').val();
			jQuery.post(base_url+"ajax/done_action" ,{task_id:task_id, main_task:main_task}, function(data){
				if(data.status == 'ok'){
					jQuery('li#'+task_id+" .status_div").html(data.status_label);
					jQuery('li#'+task_id+" .check_task").html(data.label);
					jQuery('li#'+main_task+" .status_div").html(data.main_status_result);
					jQuery('li#'+main_task+" .check_task").html(data.main_label);
					if(data.is_redo == 1){
						jQuery('#redoLi_'+task_id).removeClass('hidden-btn');
						undo_sub_click();
					}
				}
			},"json");
		});
	});
}

$('.next_main').on('click', function () {
	get_tasks('inc');
})

$('.pre_main').on('click', function () {
	get_tasks('dec');
})

$('.next_sub').on('click', function () {
	sub_parent_id = jQuery("#sub_parent_id").val();
	get_subs(sub_parent_id,'inc');
})

$('.pre_sub').on('click', function () {
	sub_parent_id = jQuery("#sub_parent_id").val();
	get_subs(sub_parent_id,'dec');
})

function filterTasks(){
	$('.filterTasks').each( function(){
		var item = this ;
		$(item).on('ifChecked', function(event){
			var filterby = jQuery(item).val();
			get_tasks('none',filterby);
		});
	});
}

function filterSubs(){
	$('.filterSubs').each( function(){
		var item = this ;
		$(item).on('ifChecked', function(event){
			sub_parent_id = jQuery("#sub_parent_id").val();
			var filterby = jQuery(item).val();
			get_subs(sub_parent_id,'none',filterby);
		});
	});
}

redpajama_set_name: RedPajamaGithub


text:
/**
 * GetConversionGoalsByTagIdsResponse.java
 * <p>
 * This file was auto-generated from WSDL
 * by the Apache Axis 1.4 Apr 22, 2006 (06:55:48 PDT) WSDL2Java emitter.
 */

package com.microsoft.bingads.v11.campaignmanagement;

public class GetConversionGoalsByTagIdsResponse implements java.io.Serializable {
    // Type metadata
    private static org.apache.axis.description.TypeDesc typeDesc =
            new org.apache.axis.description.TypeDesc(GetConversionGoalsByTagIdsResponse.class, true);

    static {
        typeDesc.setXmlType(new javax.xml.namespace.QName("https://bingads.microsoft.com/CampaignManagement/v11", ">GetConversionGoalsByTagIdsResponse"));
        org.apache.axis.description.ElementDesc elemField = new org.apache.axis.description.ElementDesc();
        elemField.setFieldName("conversionGoals");
        elemField.setXmlName(new javax.xml.namespace.QName("https://bingads.microsoft.com/CampaignManagement/v11", "ConversionGoals"));
        elemField.setXmlType(new javax.xml.namespace.QName("https://bingads.microsoft.com/CampaignManagement/v11", "ConversionGoal"));
        elemField.setMinOccurs(0);
        elemField.setNillable(true);
        elemField.setItemQName(new javax.xml.namespace.QName("https://bingads.microsoft.com/CampaignManagement/v11", "ConversionGoal"));
        typeDesc.addFieldDesc(elemField);
        elemField = new org.apache.axis.description.ElementDesc();
        elemField.setFieldName("partialErrors");
        elemField.setXmlName(new javax.xml.namespace.QName("https://bingads.microsoft.com/CampaignManagement/v11", "PartialErrors"));
        elemField.setXmlType(new javax.xml.namespace.QName("https://bingads.microsoft.com/CampaignManagement/v11", "BatchError"));
        elemField.setMinOccurs(0);
        elemField.setNillable(true);
        elemField.setItemQName(new javax.xml.namespace.QName("https://bingads.microsoft.com/CampaignManagement/v11", "BatchError"));
        typeDesc.addFieldDesc(elemField);
    }

    private com.microsoft.bingads.v11.campaignmanagement.ConversionGoal[] conversionGoals;
    private com.microsoft.bingads.v11.campaignmanagement.BatchError[] partialErrors;
    private java.lang.Object __equalsCalc = null;
    private boolean __hashCodeCalc = false;

    public GetConversionGoalsByTagIdsResponse() {
    }

    public GetConversionGoalsByTagIdsResponse(
            com.microsoft.bingads.v11.campaignmanagement.ConversionGoal[] conversionGoals,
            com.microsoft.bingads.v11.campaignmanagement.BatchError[] partialErrors) {
        this.conversionGoals = conversionGoals;
        this.partialErrors = partialErrors;
    }

    /**
     * Return type metadata object
     */
    public static org.apache.axis.description.TypeDesc getTypeDesc() {
        return typeDesc;
    }

    /**
     * Get Custom Serializer
     */
    public static org.apache.axis.encoding.Serializer getSerializer(
            java.lang.String mechType,
            java.lang.Class _javaType,
            javax.xml.namespace.QName _xmlType) {
        return
                new org.apache.axis.encoding.ser.BeanSerializer(
                        _javaType, _xmlType, typeDesc);
    }

    /**
     * Get Custom Deserializer
     */
    public static org.apache.axis.encoding.Deserializer getDeserializer(
            java.lang.String mechType,
            java.lang.Class _javaType,
            javax.xml.namespace.QName _xmlType) {
        return
                new org.apache.axis.encoding.ser.BeanDeserializer(
                        _javaType, _xmlType, typeDesc);
    }

    /**
     * Gets the conversionGoals value for this GetConversionGoalsByTagIdsResponse.
     *
     * @return conversionGoals
     */
    public com.microsoft.bingads.v11.campaignmanagement.ConversionGoal[] getConversionGoals() {
        return conversionGoals;
    }

    /**
     * Sets the conversionGoals value for this GetConversionGoalsByTagIdsResponse.
     *
     * @param conversionGoals
     */
    public void setConversionGoals(com.microsoft.bingads.v11.campaignmanagement.ConversionGoal[] conversionGoals) {
        this.conversionGoals = conversionGoals;
    }

    /**
     * Gets the partialErrors value for this GetConversionGoalsByTagIdsResponse.
     *
     * @return partialErrors
     */
    public com.microsoft.bingads.v11.campaignmanagement.BatchError[] getPartialErrors() {
        return partialErrors;
    }

    /**
     * Sets the partialErrors value for this GetConversionGoalsByTagIdsResponse.
     *
     * @param partialErrors
     */
    public void setPartialErrors(com.microsoft.bingads.v11.campaignmanagement.BatchError[] partialErrors) {
        this.partialErrors = partialErrors;
    }

    public synchronized boolean equals(java.lang.Object obj) {
        if (!(obj instanceof GetConversionGoalsByTagIdsResponse)) return false;
        GetConversionGoalsByTagIdsResponse other = (GetConversionGoalsByTagIdsResponse) obj;
        if (obj == null) return false;
        if (this == obj) return true;
        if (__equalsCalc != null) {
            return (__equalsCalc == obj);
        }
        __equalsCalc = obj;
        boolean _equals;
        _equals = true &&
                ((this.conversionGoals == null && other.getConversionGoals() == null) ||
                        (this.conversionGoals != null &&
                                java.util.Arrays.equals(this.conversionGoals, other.getConversionGoals()))) &&
                ((this.partialErrors == null && other.getPartialErrors() == null) ||
                        (this.partialErrors != null &&
                                java.util.Arrays.equals(this.partialErrors, other.getPartialErrors())));
        __equalsCalc = null;
        return _equals;
    }

    public synchronized int hashCode() {
        if (__hashCodeCalc) {
            return 0;
        }
        __hashCodeCalc = true;
        int _hashCode = 1;
        if (getConversionGoals() != null) {
            for (int i = 0;
                 i < java.lang.reflect.Array.getLength(getConversionGoals());
                 i++) {
                java.lang.Object obj = java.lang.reflect.Array.get(getConversionGoals(), i);
                if (obj != null &&
                        !obj.getClass().isArray()) {
                    _hashCode += obj.hashCode();
                }
            }
        }
        if (getPartialErrors() != null) {
            for (int i = 0;
                 i < java.lang.reflect.Array.getLength(getPartialErrors());
                 i++) {
                java.lang.Object obj = java.lang.reflect.Array.get(getPartialErrors(), i);
                if (obj != null &&
                        !obj.getClass().isArray()) {
                    _hashCode += obj.hashCode();
                }
            }
        }
        __hashCodeCalc = false;
        return _hashCode;
    }

}


text:
NSString * const A0ClientInfoHeaderName = @"Auth0-Client";
NSString * const A0ClientInfoQueryParamName = @"auth0Client";

static NSString * const Auth0FileName = @"Auth0";
static NSString * const Auth0FileExtension = @"plist";

@interface NSURL (A0Lock)

+ (instancetype)URLWithAuth0Domain:(NSString *)domain;

@end

@implementation NSURL (A0Lock)

+ (instancetype)URLWithAuth0Domain:(NSString *)domain {
    NSURL *url = [domain hasPrefix:@"http"] ? [NSURL URLWithString:domain] : [NSURL URLWithString:[@"https://" stringByAppendingString:domain]];
    return url;
}

@end

static NSURL *Auth0CDNRegionURLFromDomainURL(NSURL *domainURL) {
    NSString *cdn = @"cdn";

    if ([domainURL.host hasSuffix:@".auth0.com"]) {
        NSString *part = [[domainURL.host componentsSeparatedByString:@".auth0.com"] firstObject];
        NSArray<NSString *> *values = [part componentsSeparatedByString:@"."];
        if (values.count > 1 && values.lastObject.length > 0) {
            cdn = [[cdn stringByAppendingString:@"."] stringByAppendingString:values.lastObject];
        }
    }
    NSURLComponents *components = [[NSURLComponents alloc] init];
    components.scheme = @"https";
    components.host = [cdn stringByAppendingString:@".auth0.com"];
    return components.URL;
}

@interface A0Lock ()

@property (strong, nonatomic) id<A0APIRouter> router;
@property (strong, nonatomic) A0APIClient *client;
#if TARGET_OS_IPHONE
@property (strong, nonatomic) A0IdentityProviderAuthenticator *authenticator;
#endif

@end

@implementation A0Lock

AUTH0_DYNAMIC_LOGGER_METHODS

- (instancetype)init {
    NSString *path = [[NSBundle mainBundle] pathForResource:Auth0FileName ofType:Auth0FileExtension];
    id<A0CredentialProvider> credentialProvider;
    if (path) {
        A0LogInfo(@"Using Auth0 credentials from file %@", path);
        credentialProvider = [[A0FileCredentialProvider alloc] initWithFilePath:path];
    } else {
        A0LogInfo(@"Using Auth0 credentials from main bundle");
        credentialProvider = [[A0MainBundleCredentialProvider alloc] init];
    }
    return [self initWithCredentialProvider:credentialProvider];
}

- (instancetype)initWithCredentialProvider:(id<A0CredentialProvider>)credentialProvider {
    NSString *clientId = [credentialProvider clientId];
    NSString *domain = [credentialProvider domain];
    NSString *configurationDomain = [credentialProvider configurationDomain];
    A0LogVerbose(@"Building lock with credentials %@", credentialProvider);
    if (configurationDomain) {
        return [self initWithClientId:clientId domain:domain configurationDomain:configurationDomain];
    }
    return [self initWithClientId:clientId domain:domain];
}

- (instancetype)initWithClientId:(NSString *)clientId domain:(NSString *)domain {
    NSAssert(clientId.length > 0, @"Must supply a valid clientId");
    NSAssert(domain.length > 0, @"Must supply a valid domain");
    NSURL *domainURL = [NSURL URLWithAuth0Domain:domain];
    NSURL *configurationURL = Auth0CDNRegionURLFromDomainURL(domainURL);
    return [self initWithClientId:clientId domain:domain configurationDomain:configurationURL.absoluteString];
}

- (instancetype)initWithClientId:(NSString *)clientId domain:(NSString *)domain configurationDomain:(NSString *)configurationDomain {
    NSAssert(clientId.length > 0, @"Must supply a valid clientId");
    NSAssert(domain.length > 0, @"Must supply a valid domain");
    NSAssert(configurationDomain.length > 0, @"Must supply a valid configuration domain");
    self = [super init];
    if (self) {
        NSURL *domainURL = [NSURL URLWithAuth0Domain:domain];
        NSString *clientPath = [[@"client" stringByAppendingPathComponent:clientId] stringByAppendingPathExtension:@"js"];
        NSURL *configurationURL = [NSURL URLWithString:clientPath relativeToURL:[NSURL URLWithAuth0Domain:configurationDomain]];
        A0LogDebug(@"Auth0 Lock initialised with clientId: (%@) domainURL: (%@) configurationURL: (%@)", clientId, domainURL, configurationURL);
        _router = [[A0APIv1Router alloc] initWithClientId:clientId domainURL:domainURL configurationURL:configurationURL];
        _client = [[A0APIClient alloc] initWithAPIRouter:_router];
#if TARGET_OS_IPHONE
        _authenticator = [[A0IdentityProviderAuthenticator alloc] initWithLock:self];
#endif
        _telemetry = [A0Telemetry telemetryEnabled] ? [[A0Telemetry alloc] init] : nil;
        _client.telemetryInfo = _telemetry.base64Value;
        _usePKCE = NO;
    }
    return self;
}

+ (instancetype)sharedLock {
    static A0Lock *SharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        SharedInstance = [A0Lock newLock];
    });
    return SharedInstance;
}

- (void)setTelemetry:(A0Telemetry *)telemetry {
    [self willChangeValueForKey:NSStringFromSelector(@selector(telemetry))];
    _telemetry = telemetry;
    [self didChangeValueForKey:NSStringFromSelector(@selector(telemetry))];
    self.client.telemetryInfo = telemetry.base64Value;
}

- (A0APIClient *)apiClient {
    return self.client;
}

- (A0UserAPIClient *)newUserAPIClientWithIdToken:(NSString *)idToken {
    return [[A0UserAPIClient alloc] initWithRouter:self.router idToken:idToken];
}

- (NSString *)clientId {
    return [self.router clientId];
}

- (NSURL *)configurationURL {
    return [self.router configurationURL];
}

- (NSURL *)domainURL {
    return [self.router endpointURL];
}

#pragma mark - IdP methods iOS only

#if TARGET_OS_IPHONE

- (A0IdentityProviderAuthenticator *)identityProviderAuthenticator {
    return self.authenticator;
}

- (BOOL)handleURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication {
    return [self.identityProviderAuthenticator handleURL:url sourceApplication:sourceApplication];
}

- (void)registerAuthenticators:(NSArray *)authenticators {
    [self.identityProviderAuthenticator registerAuthenticationProviders:authenticators];
}

- (void)clearSessions {
    [self.identityProviderAuthenticator clearSessions];
}

- (void)applicationLaunchedWithOptions:(NSDictionary *)launchOptions {
    [self.identityProviderAuthenticator applicationLaunchedWithOptions:launchOptions];
}

- (BOOL)continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
    NSURL *url = userActivity.webpageURL;
    if (!url) {
        A0LogWarn(@"Received an user activity with no URL");
        return NO;
    }

    NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES];

    if (![components.host isEqualToString:self.domainURL.host]) {
        A0LogWarn(@"URL %@ is not inside configured Auth0 subdomain %@", url, self.domainURL.host);
        return NO;
    }

    NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
    NSString *prefix = [NSString stringWithFormat:@"/ios/%@", bundleIdentifier];

    if (![components.path.lowercaseString hasPrefix:prefix.lowercaseString]) {
        A0LogWarn(@"URL %@ is not for this application with bundleId %@", url, bundleIdentifier);
        return NO;
    }

    A0LogDebug(@"Received Universal Link %@", url);
    [[NSNotificationCenter defaultCenter] postNotificationName:A0LockNotificationUniversalLinkReceived
                                                        object:nil
                                                      userInfo:@{
                                                                 A0LockNotificationUniversalLinkParameterKey: url
                                                                 }];
    return YES;
}

#endif

# pragma mark - Factory methods

+ (instancetype)newLock {
    return [[A0Lock alloc] init];
}

+ (instancetype)newLockWithClientId:(NSString *)clientId domain:(NSString *)domain {
    return [[A0Lock alloc] initWithClientId:clientId domain:domain];
}

+ (instancetype)newLockWithClientId:(NSString *)clientId domain:(NSString *)domain configurationDomain:(NSString *)configurationDomain {
    return [[A0Lock alloc] initWithClientId:clientId domain:domain configurationDomain:configurationDomain];
}

@end


text:
Data Driven Automation Framework:
=================================

This project is being used to demonstrate data driven framework pattern for Test Automation in Ruby

Instruction to use
===================
1) Install Ruby (1.9 or higher)
2) gem install cucumber
3) gem install bundler
4) check out the project
5) From project root folder, "bundle exec cucumber features"

Known Issues
=====================
Hooks.rb code launches browser even for non-browser scenarios, however it closes it down too

Instructions to use
=====================

1) Checkout the project
2) Run bundle install
3) Go through .feature files
4) Check step definitions

## Contributing

1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request


text:
import {registerBidder} from 'src/adapters/bidderFactory';
import * as utils from 'src/utils';

const A4G_BIDDER_CODE = 'a4g';
const A4G_CURRENCY = 'USD';
const A4G_DEFAULT_BID_URL = '//ads.ad4game.com/v1/bid';
const A4G_TTL = 120;

const LOCATION_PARAM_NAME = 'siteurl';
const ID_PARAM_NAME = 'id';
const IFRAME_PARAM_NAME = 'if';
const ZONE_ID_PARAM_NAME = 'zoneId';
const SIZE_PARAM_NAME = 'size';

const ARRAY_PARAM_SEPARATOR = ';';
const ARRAY_SIZE_SEPARATOR = ',';
const SIZE_SEPARATOR = 'x';

export const spec = {
  code: A4G_BIDDER_CODE,
  isBidRequestValid: function(bid) {
    return bid.params && !!bid.params.zoneId;
  },

  buildRequests: function(validBidRequests) {
    let deliveryUrl = '';
    let bidId = '';
    const idParams = [];
    const sizeParams = [];
    const zoneIds = [];

    utils._each(validBidRequests, function(bid) {
      if (!deliveryUrl && typeof bid.params.deliveryUrl === 'string') {
        deliveryUrl = bid.params.deliveryUrl;
      }
      if (!bidId) {
        bidId = bid.bidId;
      }
      idParams.push(bid.placementCode);
      sizeParams.push(bid.sizes.map(size => size.join(SIZE_SEPARATOR)).join(ARRAY_SIZE_SEPARATOR));
      zoneIds.push(bid.params.zoneId);
    });

    if (!deliveryUrl) {
      deliveryUrl = A4G_DEFAULT_BID_URL;
    }

    return {
      method: 'GET',
      url: deliveryUrl,
      bidId: bidId,
      data: {
        [IFRAME_PARAM_NAME]: 0,
        [LOCATION_PARAM_NAME]: utils.getTopWindowUrl(),
        [SIZE_PARAM_NAME]: sizeParams.join(ARRAY_PARAM_SEPARATOR),
        [ID_PARAM_NAME]: idParams.join(ARRAY_PARAM_SEPARATOR),
        [ZONE_ID_PARAM_NAME]: zoneIds.join(ARRAY_PARAM_SEPARATOR)
      }
    };
  },

  interpretResponse: function(serverResponses, request) {
    const bidResponses = [];
    utils._each(serverResponses.body, function(response) {
      const bidResponse = {
        requestId: request.bidId,
        cpm: response.cpm,
        width: response.width,
        height: response.height,
        creativeId: response.zoneid,
        currency: A4G_CURRENCY,
        netRevenue: true,
        ttl: A4G_TTL,
        ad: response.ad
      };
      bidResponses.push(bidResponse);
    });

    return bidResponses;
  }
};

registerBidder(spec);


text:
<?php

return [
    'Names' => [
        'Africa/Abidjan' => 'Greenwichtid (Abidjan)',
        'Africa/Accra' => 'Greenwichtid (Accra)',
        'Africa/Addis_Ababa' => 'östafrikansk tid (Addis Abeba)',
        'Africa/Algiers' => 'centraleuropeisk tid (Alger)',
        'Africa/Asmera' => 'östafrikansk tid (Asmara)',
        'Africa/Bamako' => 'Greenwichtid (Bamako)',
        'Africa/Bangui' => 'västafrikansk tid (Bangui)',
        'Africa/Banjul' => 'Greenwichtid (Banjul)',
        'Africa/Bissau' => 'Greenwichtid (Bissau)',
        'Africa/Blantyre' => 'centralafrikansk tid (Blantyre)',
        'Africa/Brazzaville' => 'västafrikansk tid (Brazzaville)',
        'Africa/Bujumbura' => 'centralafrikansk tid (Bujumbura)',
        'Africa/Cairo' => 'östeuropeisk tid (Kairo)',
        'Africa/Casablanca' => 'västeuropeisk tid (Casablanca)',
        'Africa/Ceuta' => 'centraleuropeisk tid (Ceuta)',
        'Africa/Conakry' => 'Greenwichtid (Conakry)',
        'Africa/Dakar' => 'Greenwichtid (Dakar)',
        'Africa/Dar_es_Salaam' => 'östafrikansk tid (Dar es-Salaam)',
        'Africa/Djibouti' => 'östafrikansk tid (Djibouti)',
        'Africa/Douala' => 'västafrikansk tid (Douala)',
        'Africa/El_Aaiun' => 'västeuropeisk tid (El-Aaiún)',
        'Africa/Freetown' => 'Greenwichtid (Freetown)',
        'Africa/Gaborone' => 'centralafrikansk tid (Gaborone)',
        'Africa/Harare' => 'centralafrikansk tid (Harare)',
        'Africa/Johannesburg' => 'sydafrikansk tid (Johannesburg)',
        'Africa/Juba' => 'centralafrikansk tid (Juba)',
        'Africa/Kampala' => 'östafrikansk tid (Kampala)',
        'Africa/Khartoum' => 'centralafrikansk tid (Khartoum)',
        'Africa/Kigali' => 'centralafrikansk tid (Kigali)',
        'Africa/Kinshasa' => 'västafrikansk tid (Kinshasa)',
        'Africa/Lagos' => 'västafrikansk tid (Lagos)',
        'Africa/Libreville' => 'västafrikansk tid (Libreville)',
        'Africa/Lome' => 'Greenwichtid (Lomé)',
        'Africa/Luanda' => 'västafrikansk tid (Luanda)',
        'Africa/Lubumbashi' => 'centralafrikansk tid (Lubumbashi)',
        'Africa/Lusaka' => 'centralafrikansk tid (Lusaka)',
        'Africa/Malabo' => 'västafrikansk tid (Malabo)',
        'Africa/Maputo' => 'centralafrikansk tid (Maputo)',
        'Africa/Maseru' => 'sydafrikansk tid (Maseru)',
        'Africa/Mbabane' => 'sydafrikansk tid (Mbabane)',
        'Africa/Mogadishu' => 'östafrikansk tid (Mogadishu)',
        'Africa/Monrovia' => 'Greenwichtid (Monrovia)',
        'Africa/Nairobi' => 'östafrikansk tid (Nairobi)',
        'Africa/Ndjamena' => 'västafrikansk tid (N'Djamena)',
        'Africa/Niamey' => 'västafrikansk tid (Niamey)',
        'Africa/Nouakchott' => 'Greenwichtid (Nouakchott)',
        'Africa/Ouagadougou' => 'Greenwichtid (Ouagadougou)',
        'Africa/Porto-Novo' => 'västafrikansk tid (Porto-Novo)',
        'Africa/Sao_Tome' => 'Greenwichtid (São Tomé)',
        'Africa/Tripoli' => 'östeuropeisk tid (Tripoli)',
        'Africa/Tunis' => 'centraleuropeisk tid (Tunis)',
        'Africa/Windhoek' => 'centralafrikansk tid (Windhoek)',
        'America/Adak' => 'Honolulutid (Adak)',
        'America/Anchorage' => 'Alaskatid (Anchorage)',
        'America/Anguilla' => 'nordamerikansk atlanttid (Anguilla)',
        'America/Antigua' => 'nordamerikansk atlanttid (Antigua)',
        'America/Araguaina' => 'Brasiliatid (Araguaína)',
        'America/Argentina/La_Rioja' => 'östargentinsk tid (La Rioja)',
        'America/Argentina/Rio_Gallegos' => 'östargentinsk tid (Río Gallegos)',
        'America/Argentina/Salta' => 'östargentinsk tid (Salta)',
        'America/Argentina/San_Juan' => 'östargentinsk tid (San Juan)',
        'America/Argentina/San_Luis' => 'östargentinsk tid (San Luis)',
        'America/Argentina/Tucuman' => 'östargentinsk tid (Tucumán)',
        'America/Argentina/Ushuaia' => 'östargentinsk tid (Ushuaia)',
        'America/Aruba' => 'nordamerikansk atlanttid (Aruba)',
        'America/Asuncion' => 'paraguayansk tid (Asunción)',
        'America/Bahia' => 'Brasiliatid (Bahia)',
        'America/Bahia_Banderas' => 'centralnordamerikansk tid (Bahía de Banderas)',
        'America/Barbados' => 'nordamerikansk atlanttid (Barbados)',
        'America/Belem' => 'Brasiliatid (Belém)',
        'America/Belize' => 'centralnordamerikansk tid (Belize)',
        'America/Blanc-Sablon' => 'nordamerikansk atlanttid (Blanc-Sablon)',
        'America/Boa_Vista' => 'Amazonastid (Boa Vista)',
        'America/Bogota' => 'colombiansk tid (Bogotá)',
        'America/Boise' => 'Klippiga bergentid (Boise)',
        'America/Buenos_Aires' => 'östargentinsk tid (Buenos Aires)',
        'America/Cambridge_Bay' => 'Klippiga bergentid (Cambridge Bay)',
        'America/Campo_Grande' => 'Amazonastid (Campo Grande)',
        'America/Cancun' => 'östnordamerikansk tid (Cancún)',
        'America/Caracas' => 'venezuelansk tid (Caracas)',
        'America/Catamarca' => 'östargentinsk tid (Catamarca)',
        'America/Cayenne' => 'Franska Guyanatid (Cayenne)',
        'America/Cayman' => 'östnordamerikansk tid (Caymanöarna)',
        'America/Chicago' => 'centralnordamerikansk tid (Chicago)',
        'America/Chihuahua' => 'mexikansk stillahavstid (Chihuahua)',
        'America/Coral_Harbour' => 'östnordamerikansk tid (Atikokan)',
        'America/Cordoba' => 'östargentinsk tid (Córdoba)',
        'America/Costa_Rica' => 'centralnordamerikansk tid (Costa Rica)',
        'America/Creston' => 'Klippiga bergentid (Creston)',
        'America/Cuiaba' => 'Amazonastid (Cuiabá)',
        'America/Curacao' => 'nordamerikansk atlanttid (Curaçao)',
        'America/Danmarkshavn' => 'Greenwichtid (Danmarkshavn)',
        'America/Dawson' => 'Klippiga bergentid (Dawson)',
        'America/Dawson_Creek' => 'Klippiga bergentid (Dawson Creek)',
        'America/Denver' => 'Klippiga bergentid (Denver)',
        'America/Detroit' => 'östnordamerikansk tid (Detroit)',
        'America/Dominica' => 'nordamerikansk atlanttid (Dominica)',
        'America/Edmonton' => 'Klippiga bergentid (Edmonton)',
        'America/Eirunepe' => 'västbrasiliansk tid (Eirunepé)',
        'America/El_Salvador' => 'centralnordamerikansk tid (El Salvador)',
        'America/Fort_Nelson' => 'Klippiga bergentid (Fort Nelson)',
        'America/Fortaleza' => 'Brasiliatid (Fortaleza)',
        'America/Glace_Bay' => 'nordamerikansk atlanttid (Glace Bay)',
        'America/Godthab' => 'västgrönländsk tid (Nuuk)',
        'America/Goose_Bay' => 'nordamerikansk atlanttid (Goose Bay)',
        'America/Grand_Turk' => 'östnordamerikansk tid (Grand Turk)',
        'America/Grenada' => 'nordamerikansk atlanttid (Grenada)',
        'America/Guadeloupe' => 'nordamerikansk atlanttid (Guadeloupe)',
        'America/Guatemala' => 'centralnordamerikansk tid (Guatemala)',
        'America/Guayaquil' => 'ecuadoriansk tid (Guayaquil)',
        'America/Guyana' => 'Guyanatid',
        'America/Halifax' => 'nordamerikansk atlanttid (Halifax)',
        'America/Havana' => 'kubansk tid (Havanna)',
        'America/Hermosillo' => 'mexikansk stillahavstid (Hermosillo)',
        'America/Indiana/Knox' => 'centralnordamerikansk tid (Knox, Indiana)',
        'America/Indiana/Marengo' => 'östnordamerikansk tid (Marengo, Indiana)',
        'America/Indiana/Petersburg' => 'östnordamerikansk tid (Petersburg, Indiana)',
        'America/Indiana/Tell_City' => 'centralnordamerikansk tid (Tell City, Indiana)',
        'America/Indiana/Vevay' => 'östnordamerikansk tid (Vevay, Indiana)',
        'America/Indiana/Vincennes' => 'östnordamerikansk tid (Vincennes, Indiana)',
        'America/Indiana/Winamac' => 'östnordamerikansk tid (Winamac, Indiana)',
        'America/Indianapolis' => 'östnordamerikansk tid (Indianapolis)',
        'America/Inuvik' => 'Klippiga bergentid (Inuvik)',
        'America/Iqaluit' => 'östnordamerikansk tid (Iqaluit)',
        'America/Jamaica' => 'östnordamerikansk tid (Jamaica)',
        'America/Jujuy' => 'östargentinsk tid (San Salvador de Jujuy)',
        'America/Juneau' => 'Alaskatid (Juneau)',
        'America/Kentucky/Monticello' => 'östnordamerikansk tid (Monticello, Kentucky)',
        'America/Kralendijk' => 'nordamerikansk atlanttid (Kralendijk)',
        'America/La_Paz' => 'boliviansk tid (La Paz)',
        'America/Lima' => 'peruansk tid (Lima)',
        'America/Los_Angeles' => 'västnordamerikansk tid (Los Angeles)',
        'America/Louisville' => 'östnordamerikansk tid (Louisville)',
        'America/Lower_Princes' => 'nordamerikansk atlanttid (Lower Prince's Quarter)',
        'America/Maceio' => 'Brasiliatid (Maceió)',
        'America/Managua' => 'centralnordamerikansk tid (Managua)',
        'America/Manaus' => 'Amazonastid (Manaus)',
        'America/Marigot' => 'nordamerikansk atlanttid (Marigot)',
        'America/Martinique' => 'nordamerikansk atlanttid (Martinique)',
        'America/Matamoros' => 'centralnordamerikansk tid (Matamoros)',
        'America/Mazatlan' => 'mexikansk stillahavstid (Mazatlán)',
        'America/Mendoza' => 'östargentinsk tid (Mendoza)',
        'America/Menominee' => 'centralnordamerikansk tid (Menominee)',
        'America/Merida' => 'centralnordamerikansk tid (Mérida)',
        'America/Metlakatla' => 'Alaskatid (Metlakatla)',
        'America/Mexico_City' => 'centralnordamerikansk tid (Mexiko City)',
        'America/Miquelon' => 'S:t Pierre och Miquelontid',
        'America/Moncton' => 'nordamerikansk atlanttid (Moncton)',
        'America/Monterrey' => 'centralnordamerikansk tid (Monterrey)',
        'America/Montevideo' => 'uruguayansk tid (Montevideo)',
        'America/Montreal' => 'Kanadatid (Montreal)',
        'America/Montserrat' => 'nordamerikansk atlanttid (Montserrat)',
        'America/Nassau' => 'östnordamerikansk tid (Nassau)',
        'America/New_York' => 'östnordamerikansk tid (New York)',
        'America/Nipigon' => 'östnordamerikansk tid (Nipigon)',
        'America/Nome' => 'Alaskatid (Nome)',
        'America/Noronha' => 'Fernando de Noronhatid',
        'America/North_Dakota/Beulah' => 'centralnordamerikansk tid (Beulah, North Dakota)',
        'America/North_Dakota/Center' => 'centralnordamerikansk tid (Center, North Dakota)',
        'America/North_Dakota/New_Salem' => 'centralnordamerikansk tid (New Salem, North Dakota)',
        'America/Ojinaga' => 'Klippiga bergentid (Ojinaga)',
        'America/Panama' => 'östnordamerikansk tid (Panama)',
        'America/Pangnirtung' => 'östnordamerikansk tid (Pangnirtung)',
        'America/Paramaribo' => 'Surinamtid (Paramaribo)',
        'America/Phoenix' => 'Klippiga bergentid (Phoenix)',
        'America/Port-au-Prince' => 'östnordamerikansk tid (Port-au-Prince)',
        'America/Port_of_Spain' => 'nordamerikansk atlanttid (Port of Spain)',
        'America/Porto_Velho' => 'Amazonastid (Porto Velho)',
        'America/Puerto_Rico' => 'nordamerikansk atlanttid (Puerto Rico)',
        'America/Punta_Arenas' => 'chilensk tid (Punta Arenas)',
        'America/Rainy_River' => 'centralnordamerikansk tid (Rainy River)',
        'America/Rankin_Inlet' => 'centralnordamerikansk tid (Rankin Inlet)',
        'America/Recife' => 'Brasiliatid (Recife)',
        'America/Regina' => 'centralnordamerikansk tid (Regina)',
        'America/Resolute' => 'centralnordamerikansk tid (Resolute)',
        'America/Rio_Branco' => 'västbrasiliansk tid (Rio Branco)',
        'America/Santa_Isabel' => 'nordvästmexikansk tid (Santa Isabel)',
        'America/Santarem' => 'Brasiliatid (Santarém)',
        'America/Santiago' => 'chilensk tid (Santiago)',
        'America/Santo_Domingo' => 'nordamerikansk atlanttid (Santo Domingo)',
        'America/Sao_Paulo' => 'Brasiliatid (São Paulo)',
        'America/Scoresbysund' => 'östgrönländsk tid (Ittoqqortoormiit)',
        'America/Sitka' => 'Alaskatid (Sitka)',
        'America/St_Barthelemy' => 'nordamerikansk atlanttid (S:t Barthélemy)',
        'America/St_Johns' => 'Newfoundlandtid (S:t Johns)',
        'America/St_Kitts' => 'nordamerikansk atlanttid (S:t Kitts)',
        'America/St_Lucia' => 'nordamerikansk atlanttid (S:t Lucia)',
        'America/St_Thomas' => 'nordamerikansk atlanttid (S:t Thomas)',
        'America/St_Vincent' => 'nordamerikansk atlanttid (S:t Vincent)',
        'America/Swift_Current' => 'centralnordamerikansk tid (Swift Current)',
        'America/Tegucigalpa' => 'centralnordamerikansk tid (Tegucigalpa)',
        'America/Thule' => 'nordamerikansk atlanttid (Qaanaaq)',
        'America/Thunder_Bay' => 'östnordamerikansk tid (Thunder Bay)',
        'America/Tijuana' => 'västnordamerikansk tid (Tijuana)',
        'America/Toronto' => 'östnordamerikansk tid (Toronto)',
        'America/Tortola' => 'nordamerikansk atlanttid (Tortola)',
        'America/Vancouver' => 'västnordamerikansk tid (Vancouver)',
        'America/Whitehorse' => 'Klippiga bergentid (Whitehorse)',
        'America/Winnipeg' => 'centralnordamerikansk tid (Winnipeg)',
        'America/Yakutat' => 'Alaskatid (Yakutat)',
        'America/Yellowknife' => 'Klippiga bergentid (Yellowknife)',
        'Antarctica/Casey' => 'Caseytid',
        'Antarctica/Davis' => 'Davistid',
        'Antarctica/DumontDUrville' => 'Dumont d'Urville-tid',
        'Antarctica/Macquarie' => 'östaustralisk tid (Macquarie)',
        'Antarctica/Mawson' => 'Mawsontid',
        'Antarctica/McMurdo' => 'nyzeeländsk tid (McMurdo)',
        'Antarctica/Palmer' => 'chilensk tid (Palmer)',
        'Antarctica/Rothera' => 'Rotheratid',
        'Antarctica/Syowa' => 'Syowatid',
        'Antarctica/Troll' => 'Greenwichtid (Troll)',
        'Antarctica/Vostok' => 'Vostoktid',
        'Arctic/Longyearbyen' => 'centraleuropeisk tid (Longyearbyen)',
        'Asia/Aden' => 'saudiarabisk tid (Aden)',
        'Asia/Almaty' => 'östkazakstansk tid (Almaty)',
        'Asia/Amman' => 'östeuropeisk tid (Amman)',
        'Asia/Anadyr' => 'Anadyrtid',
        'Asia/Aqtau' => 'västkazakstansk tid (Aktau)',
        'Asia/Aqtobe' => 'västkazakstansk tid (Aqtöbe)',
        'Asia/Ashgabat' => 'turkmensk tid (Asjchabad)',
        'Asia/Atyrau' => 'västkazakstansk tid (Atyrau)',
        'Asia/Baghdad' => 'saudiarabisk tid (Bagdad)',
        'Asia/Bahrain' => 'saudiarabisk tid (Bahrain)',
        'Asia/Baku' => 'azerbajdzjansk tid (Baku)',
        'Asia/Bangkok' => 'indokinesisk tid (Bangkok)',
        'Asia/Barnaul' => 'Rysslandtid (Barnaul)',
        'Asia/Beirut' => 'östeuropeisk tid (Beirut)',
        'Asia/Bishkek' => 'kirgizisk tid (Bisjkek)',
        'Asia/Brunei' => 'Bruneitid',
        'Asia/Calcutta' => 'indisk tid (Kolkata)',
        'Asia/Chita' => 'Jakutsktid (Tjita)',
        'Asia/Choibalsan' => 'Ulaanbaatartid (Tjojbalsan)',
        'Asia/Colombo' => 'indisk tid (Colombo)',
        'Asia/Damascus' => 'östeuropeisk tid (Damaskus)',
        'Asia/Dhaka' => 'bangladeshisk tid (Dhaka)',
        'Asia/Dili' => 'östtimorisk tid (Dili)',
        'Asia/Dubai' => 'Persiska vikentid (Dubai)',
        'Asia/Dushanbe' => 'Tadzjikistantid (Dusjanbe)',
        'Asia/Famagusta' => 'östeuropeisk tid (Famagusta)',
        'Asia/Gaza' => 'östeuropeisk tid (Gaza)',
        'Asia/Hebron' => 'östeuropeisk tid (Hebron)',
        'Asia/Hong_Kong' => 'Hongkongtid',
        'Asia/Hovd' => 'Chovdtid',
        'Asia/Irkutsk' => 'Irkutsktid',
        'Asia/Jakarta' => 'västindonesisk tid (Jakarta)',
        'Asia/Jayapura' => 'östindonesisk tid (Jayapura)',
        'Asia/Jerusalem' => 'israelisk tid (Jerusalem)',
        'Asia/Kabul' => 'afghansk tid (Kabul)',
        'Asia/Kamchatka' => 'Kamtjatkatid',
        'Asia/Karachi' => 'pakistansk tid (Karachi)',
        'Asia/Katmandu' => 'nepalesisk tid (Katmandu)',
        'Asia/Khandyga' => 'Jakutsktid (Chandyga)',
        'Asia/Krasnoyarsk' => 'Krasnojarsktid',
        'Asia/Kuala_Lumpur' => 'malaysisk tid (Kuala Lumpur)',
        'Asia/Kuching' => 'malaysisk tid (Kuching)',
        'Asia/Kuwait' => 'saudiarabisk tid (Kuwait)',
        'Asia/Macau' => 'kinesisk tid (Macao)',
        'Asia/Magadan' => 'Magadantid',
        'Asia/Makassar' => 'centralindonesisk tid (Makassar)',
        'Asia/Manila' => 'filippinsk tid (Manilla)',
        'Asia/Muscat' => 'Persiska vikentid (Muskat)',
        'Asia/Nicosia' => 'östeuropeisk tid (Nicosia)',
        'Asia/Novokuznetsk' => 'Krasnojarsktid (Novokuznetsk)',
        'Asia/Novosibirsk' => 'Novosibirsktid',
        'Asia/Omsk' => 'Omsktid',
        'Asia/Oral' => 'västkazakstansk tid (Oral)',
        'Asia/Phnom_Penh' => 'indokinesisk tid (Phnom Penh)',
        'Asia/Pontianak' => 'västindonesisk tid (Pontianak)',
        'Asia/Pyongyang' => 'koreansk tid (Pyongyang)',
        'Asia/Qatar' => 'saudiarabisk tid (Qatar)',
        'Asia/Qostanay' => 'östkazakstansk tid (Kostanaj)',
        'Asia/Qyzylorda' => 'västkazakstansk tid (Qyzylorda)',
        'Asia/Rangoon' => 'burmesisk tid (Rangoon)',
        'Asia/Riyadh' => 'saudiarabisk tid (Riyadh)',
        'Asia/Saigon' => 'indokinesisk tid (Ho Chi Minh-staden)',
        'Asia/Sakhalin' => 'Sachalintid',
        'Asia/Samarkand' => 'uzbekisk tid (Samarkand)',
        'Asia/Seoul' => 'koreansk tid (Söul)',
        'Asia/Shanghai' => 'kinesisk tid (Shanghai)',
        'Asia/Singapore' => 'Singaporetid',
        'Asia/Srednekolymsk' => 'Magadantid (Srednekolymsk)',
        'Asia/Taipei' => 'Taipeitid',
        'Asia/Tashkent' => 'uzbekisk tid (Tasjkent)',
        'Asia/Tbilisi' => 'georgisk tid (Tbilisi)',
        'Asia/Tehran' => 'iransk tid (Teheran)',
        'Asia/Thimphu' => 'bhutansk tid (Thimphu)',
        'Asia/Tokyo' => 'japansk tid (Tokyo)',
        'Asia/Tomsk' => 'Rysslandtid (Tomsk)',
        'Asia/Ulaanbaatar' => 'Ulaanbaatartid',
        'Asia/Urumqi' => 'Kinatid (Ürümqi)',
        'Asia/Ust-Nera' => 'Vladivostoktid (Ust-Nera)',
        'Asia/Vientiane' => 'indokinesisk tid (Vientiane)',
        'Asia/Vladivostok' => 'Vladivostoktid',
        'Asia/Yakutsk' => 'Jakutsktid',
        'Asia/Yekaterinburg' => 'Jekaterinburgtid',
        'Asia/Yerevan' => 'armenisk tid (Jerevan)',
        'Atlantic/Azores' => 'azorisk tid (Azorerna)',
        'Atlantic/Bermuda' => 'nordamerikansk atlanttid (Bermuda)',
        'Atlantic/Canary' => 'västeuropeisk tid (Kanarieöarna)',
        'Atlantic/Cape_Verde' => 'Kap Verdetid',
        'Atlantic/Faeroe' => 'västeuropeisk tid (Torshamn)',
        'Atlantic/Madeira' => 'västeuropeisk tid (Madeira)',
        'Atlantic/Reykjavik' => 'Greenwichtid (Reykjavik)',
        'Atlantic/South_Georgia' => 'sydgeorgisk tid (Sydgeorgien)',
        'Atlantic/St_Helena' => 'Greenwichtid (S:t Helena)',
        'Atlantic/Stanley' => 'Falklandsöarnas tid (Stanley)',
        'Australia/Adelaide' => 'centralaustralisk tid (Adelaide)',
        'Australia/Brisbane' => 'östaustralisk tid (Brisbane)',
        'Australia/Broken_Hill' => 'centralaustralisk tid (Broken Hill)',
        'Australia/Currie' => 'östaustralisk tid (Currie)',
        'Australia/Darwin' => 'centralaustralisk tid (Darwin)',
        'Australia/Eucla' => 'västcentralaustralisk tid (Eucla)',
        'Australia/Hobart' => 'östaustralisk tid (Hobart)',
        'Australia/Lindeman' => 'östaustralisk tid (Lindeman)',
        'Australia/Lord_Howe' => 'Lord Howetid',
        'Australia/Melbourne' => 'östaustralisk tid (Melbourne)',
        'Australia/Perth' => 'västaustralisk tid (Perth)',
        'Australia/Sydney' => 'östaustralisk tid (Sydney)',
        'CST6CDT' => 'centralnordamerikansk tid',
        'EST5EDT' => 'östnordamerikansk tid',
        'Etc/GMT' => 'Greenwichtid',
        'Etc/UTC' => 'koordinerad universell tid',
        'Europe/Amsterdam' => 'centraleuropeisk tid (Amsterdam)',
        'Europe/Andorra' => 'centraleuropeisk tid (Andorra)',
        'Europe/Astrakhan' => 'Moskvatid (Astrakhan)',
        'Europe/Athens' => 'östeuropeisk tid (Aten)',
        'Europe/Belgrade' => 'centraleuropeisk tid (Belgrad)',
        'Europe/Berlin' => 'centraleuropeisk tid (Berlin)',
        'Europe/Bratislava' => 'centraleuropeisk tid (Bratislava)',
        'Europe/Brussels' => 'centraleuropeisk tid (Bryssel)',
        'Europe/Bucharest' => 'östeuropeisk tid (Bukarest)',
        'Europe/Budapest' => 'centraleuropeisk tid (Budapest)',
        'Europe/Busingen' => 'centraleuropeisk tid (Büsingen am Hochrhein)',
        'Europe/Chisinau' => 'östeuropeisk tid (Chișinău)',
        'Europe/Copenhagen' => 'centraleuropeisk tid (Köpenhamn)',
        'Europe/Dublin' => 'Greenwichtid (Dublin)',
        'Europe/Gibraltar' => 'centraleuropeisk tid (Gibraltar)',
        'Europe/Guernsey' => 'Greenwichtid (Guernsey)',
        'Europe/Helsinki' => 'östeuropeisk tid (Helsingfors)',
        'Europe/Isle_of_Man' => 'Greenwichtid (Isle of Man)',
        'Europe/Istanbul' => 'Turkiettid (Istanbul)',
        'Europe/Jersey' => 'Greenwichtid (Jersey)',
        'Europe/Kaliningrad' => 'östeuropeisk tid (Kaliningrad)',
        'Europe/Kiev' => 'östeuropeisk tid (Kiev)',
        'Europe/Kirov' => 'Rysslandtid (Kirov)',
        'Europe/Lisbon' => 'västeuropeisk tid (Lissabon)',
        'Europe/Ljubljana' => 'centraleuropeisk tid (Ljubljana)',
        'Europe/London' => 'Greenwichtid (London)',
        'Europe/Luxembourg' => 'centraleuropeisk tid (Luxemburg)',
        'Europe/Madrid' => 'centraleuropeisk tid (Madrid)',
        'Europe/Malta' => 'centraleuropeisk tid (Malta)',
        'Europe/Mariehamn' => 'östeuropeisk tid (Mariehamn)',
        'Europe/Minsk' => 'Moskvatid (Minsk)',
        'Europe/Monaco' => 'centraleuropeisk tid (Monaco)',
        'Europe/Moscow' => 'Moskvatid',
        'Europe/Oslo' => 'centraleuropeisk tid (Oslo)',
        'Europe/Paris' => 'centraleuropeisk tid (Paris)',
        'Europe/Podgorica' => 'centraleuropeisk tid (Podgorica)',
        'Europe/Prague' => 'centraleuropeisk tid (Prag)',
        'Europe/Riga' => 'östeuropeisk tid (Riga)',
        'Europe/Rome' => 'centraleuropeisk tid (Rom)',
        'Europe/Samara' => 'Samaratid',
        'Europe/San_Marino' => 'centraleuropeisk tid (San Marino)',
        'Europe/Sarajevo' => 'centraleuropeisk tid (Sarajevo)',
        'Europe/Saratov' => 'Moskvatid (Saratov)',
        'Europe/Simferopol' => 'Moskvatid (Simferopol)',
        'Europe/Skopje' => 'centraleuropeisk tid (Skopje)',
        'Europe/Sofia' => 'östeuropeisk tid (Sofia)',
        'Europe/Stockholm' => 'centraleuropeisk tid (Stockholm)',
        'Europe/Tallinn' => 'östeuropeisk tid (Tallinn)',
        'Europe/Tirane' => 'centraleuropeisk tid (Tirana)',
        'Europe/Ulyanovsk' => 'Moskvatid (Uljanovsk)',
        'Europe/Uzhgorod' => 'östeuropeisk tid (Uzjhorod)',
        'Europe/Vaduz' => 'centraleuropeisk tid (Vaduz)',
        'Europe/Vatican' => 'centraleuropeisk tid (Vatikanen)',
        'Europe/Vienna' => 'centraleuropeisk tid (Wien)',
        'Europe/Vilnius' => 'östeuropeisk tid (Vilnius)',
        'Europe/Volgograd' => 'Volgogradtid',
        'Europe/Warsaw' => 'centraleuropeisk tid (Warszawa)',
        'Europe/Zagreb' => 'centraleuropeisk tid (Zagreb)',
        'Europe/Zaporozhye' => 'östeuropeisk tid (Zaporizjzja)',
        'Europe/Zurich' => 'centraleuropeisk tid (Zürich)',
        'Indian/Antananarivo' => 'östafrikansk tid (Antananarivo)',
        'Indian/Chagos' => 'Brittiska Indiska oceanöarnas tid (Chagosöarna)',
        'Indian/Christmas' => 'Julöns tid',
        'Indian/Cocos' => 'Keelingöarnas tid (Kokosöarna)',
        'Indian/Comoro' => 'östafrikansk tid (Komorerna)',
        'Indian/Kerguelen' => 'Franska Sydterritoriernas tid (Kerguelenöarna)',
        'Indian/Mahe' => 'Seychellernatid (Mahé)',
        'Indian/Maldives' => 'Maldivernatid',
        'Indian/Mauritius' => 'Mauritiustid',
        'Indian/Mayotte' => 'östafrikansk tid (Mayotte)',
        'Indian/Reunion' => 'Réuniontid',
        'MST7MDT' => 'Klippiga bergentid',
        'PST8PDT' => 'västnordamerikansk tid',
        'Pacific/Apia' => 'Apiatid',
        'Pacific/Auckland' => 'nyzeeländsk tid (Auckland)',
        'Pacific/Bougainville' => 'Papua Nya Guineas tid (Bougainville)',
        'Pacific/Chatham' => 'Chathamtid',
        'Pacific/Easter' => 'Påskötid (Påskön)',
        'Pacific/Efate' => 'Vanuatutid (Efate)',
        'Pacific/Enderbury' => 'Enderburytid',
        'Pacific/Fakaofo' => 'Tokelautid (Fakaofo)',
        'Pacific/Fiji' => 'Fijitid',
        'Pacific/Funafuti' => 'Tuvalutid (Funafuti)',
        'Pacific/Galapagos' => 'Galápagostid',
        'Pacific/Gambier' => 'Gambiertid (Gambieröarna)',
        'Pacific/Guadalcanal' => 'Salomonöarnas tid (Guadalcanal)',
        'Pacific/Guam' => 'Chamorrotid (Guam)',
        'Pacific/Honolulu' => 'Honolulutid',
        'Pacific/Johnston' => 'Honolulutid (Johnstonatollen)',
        'Pacific/Kiritimati' => 'Lineöarnas tid (Kiritimati)',
        'Pacific/Kosrae' => 'Kosraetid',
        'Pacific/Kwajalein' => 'Marshallöarnas tid (Kwajalein)',
        'Pacific/Majuro' => 'Marshallöarnas tid (Majuro)',
        'Pacific/Marquesas' => 'Marquesastid (Marquesasöarna)',
        'Pacific/Midway' => 'samoansk tid (Midwayöarna)',
        'Pacific/Nauru' => 'Naurutid',
        'Pacific/Niue' => 'Niuetid',
        'Pacific/Norfolk' => 'Norfolköns tid',
        'Pacific/Noumea' => 'Nya Kaledonientid (Nouméa)',
        'Pacific/Pago_Pago' => 'samoansk tid (Pago Pago)',
        'Pacific/Palau' => 'Palautid',
        'Pacific/Pitcairn' => 'Pitcairntid (Pitcairnöarna)',
        'Pacific/Ponape' => 'Ponapetid (Pohnpei)',
        'Pacific/Port_Moresby' => 'Papua Nya Guineas tid (Port Moresby)',
        'Pacific/Rarotonga' => 'Cooköarnas tid (Rarotonga)',
        'Pacific/Saipan' => 'Chamorrotid (Saipan)',
        'Pacific/Tahiti' => 'Tahititid',
        'Pacific/Tarawa' => 'Kiribatitid (Tarawa)',
        'Pacific/Tongatapu' => 'Tongatid (Tongatapu)',
        'Pacific/Truk' => 'Chuuktid',
        'Pacific/Wake' => 'Wakeöarnas tid',
        'Pacific/Wallis' => 'Wallis- och Futunaöarnas tid (Wallisön)',
    ],
    'Meta' => [
        'HourFormatNeg' => '−%02d:%02d',
    ],
];


text:
use crate::{DataLinkReceiver, DataLinkSender, MacAddr, NetworkInterface};

use std::io;
use std::sync::mpsc::{self, Receiver, Sender};
use std::thread;
use std::time;

/// Configuration for the dummy datalink backend. Contains `std::sync::mpsc`
/// channels that are used to communicate with the fake network.
#[derive(Debug)]
pub struct Config {
    receiver: Receiver<io::Result<Box<[u8]>>>,
    inject_handle: Option<Sender<io::Result<Box<[u8]>>>>,

    sender: Sender<Box<[u8]>>,
    read_handle: Option<Receiver<Box<[u8]>>>,
}

impl Config {
    /// Creates a new `Config` with the given channels as the backing network.
    /// When using this constructor `inject_handle` and `read_handle` will return `None`.
    /// Those handles must be kept track of elsewhere.
    ///
    /// The `DataLinkReceiver` created by the dummy backend will read packets from
    /// `receiver`. Both network errors and data can be sent on this channel.
    /// When the `receiver` channel is closed (`Sender` is dropped)
    /// `DataLinkReceiver::next()` will sleep forever, simlating an idle network.
    ///
    /// The `DataLinkSender` created by the dummy backend will send all packets sent
    /// through `build_and_send()` and `send_to()` to the `sender` channel.
    pub fn new(receiver: Receiver<io::Result<Box<[u8]>>>, sender: Sender<Box<[u8]>>) -> Config {
        Config {
            receiver: receiver,
            inject_handle: None,
            sender: sender,
            read_handle: None,
        }
    }

    /// Get the `Sender` handle that can inject packets in the fake network.
    /// Only usable with `Config`s generated from `default()`.
    pub fn inject_handle(&mut self) -> Option<Sender<io::Result<Box<[u8]>>>> {
        self.inject_handle.take()
    }

    /// Get the `Receiver` handle where packets sent to the fake network can be read.
    /// Only usable with `Config`s generated from `default()`.
    pub fn read_handle(&mut self) -> Option<Receiver<Box<[u8]>>> {
        self.read_handle.take()
    }
}

impl<'a> From<&'a super::Config> for Config {
    /// Will not use the `super::Config`. This will simply call `dummy::Config::default()`.
    fn from(_config: &super::Config) -> Config {
        Config::default()
    }
}

impl Default for Config {
    /// Creates a default config with one input and one output channel. The handles used to inject
    /// to and read form the network can be fetched with `inject_handle()` and `read_handle()`.
    fn default() -> Config {
        let (in_tx, in_rx) = mpsc::channel();
        let (out_tx, out_rx) = mpsc::channel();
        Config {
            receiver: in_rx,
            inject_handle: Some(in_tx),
            sender: out_tx,
            read_handle: Some(out_rx),
        }
    }
}

/// Create a data link channel backed by FIFO queues. Useful for debugging and testing.
/// See `Config` for how to inject and read packets on this fake network.
pub fn channel(_: &NetworkInterface, config: Config) -> io::Result<super::Channel> {
    let sender = Box::new(MockDataLinkSender {
        sender: config.sender,
    });
    let receiver = Box::new(MockDataLinkReceiver {
        receiver: config.receiver,
        used_packets: Vec::new(),
    });

    Ok(super::Channel::Ethernet(sender, receiver))
}

struct MockDataLinkSender {
    sender: Sender<Box<[u8]>>,
}

impl DataLinkSender for MockDataLinkSender {
    fn build_and_send(
        &mut self,
        num_packets: usize,
        packet_size: usize,
        func: &mut dyn FnMut(&mut [u8]),
    ) -> Option<io::Result<()>> {
        for _ in 0..num_packets {
            let mut buffer = vec![0; packet_size];
            func(&mut buffer);
            // Send the data to the queue. Don't care if it's closed
            self.sender.send(buffer.into_boxed_slice()).unwrap_or(());
        }
        Some(Ok(()))
    }

    fn send_to(&mut self, packet: &[u8], _dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
        let buffer = packet.to_vec();
        self.sender.send(buffer.into_boxed_slice()).unwrap_or(());
        Some(Ok(()))
    }
}

struct MockDataLinkReceiver {
    receiver: Receiver<io::Result<Box<[u8]>>>,
    used_packets: Vec<Box<[u8]>>,
}

impl DataLinkReceiver for MockDataLinkReceiver {
    fn next(&mut self) -> io::Result<&[u8]> {
        match self.receiver.recv() {
            Ok(result) => {
                // A network event happened. Might be a packet or a simulated error
                match result {
                    Ok(buffer) => {
                        self.used_packets.push(buffer);
                        let buffer_ref = &*self.used_packets[self.used_packets.len() - 1];
                        Ok(buffer_ref)
                    }
                    Err(e) => Err(e),
                }
            }
            Err(_) => {
                // The channel supplying fake packets is broken. The user lost/destroyed their
                // inject_handle. This means there will never be any more packets sent to this
                // dummy network. To simulate an idle network we block and sleep forever here.
                loop {
                    thread::sleep(time::Duration::new(10, 0));
                }
            }
        }
    }
}

/// Get three fake interfaces generated with `dummy_interface(0..3)`.
pub fn interfaces() -> Vec<NetworkInterface> {
    (0..3).map(|i| dummy_interface(i)).collect()
}

/// Generates a fake `NetworkInterface`.
/// The name of the interface will be `ethX` where X is the integer `i`.
/// The index will be `i`.
/// The MAC will be `01:02:03:04:05:i`.
pub fn dummy_interface(i: u8) -> NetworkInterface {
    NetworkInterface {
        name: format!("eth{}", i),
        description: "".to_string(),
        index: i as u32,
        mac: Some(MacAddr::new(1, 2, 3, 4, 5, i)),
        ips: Vec::new(),
        flags: 0,
    }
}

#[cfg(test)]
mod tests {
    use crate::{DataLinkReceiver, DataLinkSender};

    use std::io;
    use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
    use std::thread::{sleep, spawn};
    use std::time::Duration;

    #[test]
    fn send_nothing() {
        let (_, read_handle, mut tx, _) = create_net();
        // Check that sending zero packets yields zero packets
        let mut builder = |_: &mut [u8]| {
            panic!("Should not be called");
        };
        tx.build_and_send(0, 20, &mut builder).unwrap().unwrap();
        assert!(read_handle.try_recv().is_err());
    }

    #[test]
    fn send_one_packet() {
        let (_, read_handle, mut tx, _) = create_net();
        // Check that sending one packet yields one packet
        let mut builder = |pkg: &mut [u8]| {
            assert_eq!(pkg.len(), 20);
            pkg[0] = 9;
            pkg[19] = 201;
        };
        tx.build_and_send(1, 20, &mut builder).unwrap().unwrap();
        let pkg = read_handle
            .try_recv()
            .expect("Expected one packet to be sent");
        assert!(read_handle.try_recv().is_err());
        assert_eq!(pkg.len(), 20);
        assert_eq!(pkg[0], 9);
        assert_eq!(pkg[19], 201);
    }

    #[test]
    fn send_multiple_packets() {
        let (_, read_handle, mut tx, _) = create_net();
        // Check that sending multiple packets does the correct thing
        let mut closure_counter = 0;
        let mut builder = |pkg: &mut [u8]| {
            pkg[0] = closure_counter;
            closure_counter += 1;
        };
        tx.build_and_send(3, 20, &mut builder).unwrap().unwrap();
        for i in 0..3 {
            let pkg = read_handle.try_recv().expect("Expected a packet");
            assert_eq!(pkg[0], i);
        }
        assert!(read_handle.try_recv().is_err());
    }

    #[test]
    fn send_to() {
        let (_, read_handle, mut tx, _) = create_net();
        let mut buffer = vec![0; 20];
        buffer[1] = 34;
        buffer[18] = 76;

        tx.send_to(&buffer, None).unwrap().unwrap();
        let pkg = read_handle
            .try_recv()
            .expect("Expected one packet to be sent");
        assert!(read_handle.try_recv().is_err());
        assert_eq!(pkg.len(), 20);
        assert_eq!(pkg[1], 34);
        assert_eq!(pkg[18], 76);
    }

    #[test]
    fn read_nothing() {
        let (_, _, _, mut rx) = create_net();
        let (control_tx, control_rx) = mpsc::channel();
        spawn(move || {
            rx.next().expect("Should not happen 1");
            control_tx.send(()).expect("Should not happen 2");
        });
        sleep(Duration::new(0, 1_000_000));
        match control_rx.try_recv() {
            Ok(_) => panic!("Nothing should have arrived"),
            Err(TryRecvError::Disconnected) => panic!("Thread should not have quit"),
            Err(TryRecvError::Empty) => (),
        }
    }

    #[test]
    fn read_one_pkg() {
        let (inject_handle, _, _, mut rx) = create_net();

        let buffer = vec![0; 20];
        inject_handle.send(Ok(buffer.into_boxed_slice())).unwrap();

        let pkg = rx.next().expect("Expected a packet");
        assert_eq!(pkg.len(), 20);
    }

    #[test]
    fn read_multiple_pkgs() {
        let (inject_handle, _, _, mut rx) = create_net();

        for i in 0..3 {
            let buffer = vec![i; 20];
            inject_handle.send(Ok(buffer.into_boxed_slice())).unwrap();
        }

        {
            let pkg1 = rx.next().expect("Expected a packet");
            assert_eq!(pkg1[0], 0);
        }
        {
            let pkg2 = rx.next().expect("Expected a packet");
            assert_eq!(pkg2[0], 1);
        }
        {
            let pkg3 = rx.next().expect("Expected a packet");
            assert_eq!(pkg3[0], 2);
        }
    }

    fn create_net() -> (
        Sender<io::Result<Box<[u8]>>>,
        Receiver<Box<[u8]>>,
        Box<dyn DataLinkSender>,
        Box<dyn DataLinkReceiver>,
    ) {
        let interface = super::dummy_interface(56);
        let mut config = super::Config::default();
        let inject_handle = config.inject_handle().unwrap();
        let read_handle = config.read_handle().unwrap();

        let channel = super::channel(&interface, config);
        let (tx, rx) = match channel {
            Ok(super::super::Channel::Ethernet(tx, rx)) => (tx, rx),
            _ => panic!("Not a valid channel returned"),
        };
        (inject_handle, read_handle, tx, rx)
    }
}


text:
package org.apache.pinot.spi.stream;

import com.google.common.base.Preconditions;
import org.apache.pinot.spi.utils.EqualityUtils;
import org.apache.pinot.spi.utils.TimeUtils;

/**
 * Defines and builds the offset criteria for consumption from a stream
 */
public class OffsetCriteria {

  public static final OffsetCriteria SMALLEST_OFFSET_CRITERIA =
      new OffsetCriteria.OffsetCriteriaBuilder().withOffsetSmallest();

  /**
   * Enumerates the supported offset types
   */
  private enum OffsetType {

    // Consumes from the smallest available offset
    SMALLEST,

    // Consumes from the largest seen offset
    LARGEST,

    // Consumes from the time as provided in the period string
    PERIOD,

    // Consumes from the custom offset criteria */
    CUSTOM

  }

  private OffsetType _offsetType;
  private String _offsetString;

  private OffsetCriteria() {

  }

  private void setOffsetType(OffsetType offsetType) {
    _offsetType = offsetType;
  }

  private void setOffsetString(String offsetString) {
    _offsetString = offsetString;
  }

  /**
   * True if the offset criteria is defined to read from smallest available offset
   * @return
   */
  public boolean isSmallest() {
    return _offsetType != null && _offsetType.equals(OffsetType.SMALLEST);
  }

  /**
   * True if the offset criteria is defined to read from the largest known offset
   * @return
   */
  public boolean isLargest() {
    return _offsetType != null && _offsetType.equals(OffsetType.LARGEST);
  }

  /**
   * True if the offset criteria is defined as a period string
   * @return
   */
  public boolean isPeriod() {
    return _offsetType != null && _offsetType.equals(OffsetType.PERIOD);
  }

  /**
   * True if the offset criteria is defined as a custom format string
   * @return
   */
  public boolean isCustom() {
    return _offsetType != null && _offsetType.equals(OffsetType.CUSTOM);
  }

  /**
   * Getter for offset string
   * @return
   */
  public String getOffsetString() {
    return _offsetString;
  }

  /**
   * Builds an {@link OffsetCriteria}
   */
  public static class OffsetCriteriaBuilder {
    private OffsetCriteria _offsetCriteria;

    public OffsetCriteriaBuilder() {
      _offsetCriteria = new OffsetCriteria();
    }

    /**
     * Builds an {@link OffsetCriteria} with {@link OffsetType} SMALLEST
     * @return
     */
    public OffsetCriteria withOffsetSmallest() {
      _offsetCriteria.setOffsetType(OffsetType.SMALLEST);
      _offsetCriteria.setOffsetString(OffsetType.SMALLEST.toString().toLowerCase());
      return _offsetCriteria;
    }

    /**
     * Builds an {@link OffsetCriteria} with {@link OffsetType} LARGEST
     * @return
     */
    public OffsetCriteria withOffsetLargest() {
      _offsetCriteria.setOffsetType(OffsetType.LARGEST);
      _offsetCriteria.setOffsetString(OffsetType.LARGEST.toString().toLowerCase());
      return _offsetCriteria;
    }

    /**
     * Builds an {@link OffsetCriteria} with {@link OffsetType} PERIOD
     * @param periodString
     * @return
     */
    public OffsetCriteria withOffsetAsPeriod(String periodString) {
      Preconditions.checkNotNull(periodString, "Must provide period string eg. 10d, 4h30m etc");
      _offsetCriteria.setOffsetType(OffsetType.PERIOD);
      _offsetCriteria.setOffsetString(periodString);
      return _offsetCriteria;
    }

    /**
     * Builds an {@link OffsetCriteria} with {@link OffsetType} CUSTOM
     * @param customString
     * @return
     */
    public OffsetCriteria withOffsetCustom(String customString) {
      Preconditions.checkNotNull(customString, "Must provide custom offset criteria string");
      _offsetCriteria.setOffsetType(OffsetType.CUSTOM);
      _offsetCriteria.setOffsetString(customString);
      return _offsetCriteria;
    }

    /**
     * Builds an {@link OffsetCriteria} with the right {@link OffsetType} given the offset string
     * @param offsetString
     * @return
     */
    public OffsetCriteria withOffsetString(String offsetString) {
      Preconditions.checkNotNull(offsetString, "Must provide offset string");

      if (offsetString.equalsIgnoreCase(OffsetType.SMALLEST.toString())) {
        _offsetCriteria.setOffsetType(OffsetType.SMALLEST);
      } else if (offsetString.equalsIgnoreCase(OffsetType.LARGEST.toString())) {
        _offsetCriteria.setOffsetType(OffsetType.LARGEST);
      } else {
        try {
          Long periodToMillis = TimeUtils.convertPeriodToMillis(offsetString);
          if (periodToMillis >= 0) {
            _offsetCriteria.setOffsetType(OffsetType.PERIOD);
          } else {
            _offsetCriteria.setOffsetType(OffsetType.CUSTOM);
          }
        } catch (Exception e) {
          _offsetCriteria.setOffsetType(OffsetType.CUSTOM);
        }
      }
      _offsetCriteria.setOffsetString(offsetString);
      return _offsetCriteria;
    }
  }

  @Override
  public boolean equals(Object o) {
    if (EqualityUtils.isSameReference(this, o)) {
      return true;
    }

    if (EqualityUtils.isNullOrNotSameClass(this, o)) {
      return false;
    }

    OffsetCriteria that = (OffsetCriteria) o;

    return EqualityUtils.isEqual(_offsetType, that._offsetType) && EqualityUtils
        .isEqual(_offsetString, that._offsetString);
  }

  @Override
  public int hashCode() {
    int result = EqualityUtils.hashCodeOf(_offsetType);
    result = EqualityUtils.hashCodeOf(result, _offsetString);
    return result;
  }

  @Override
  public String toString() {
    return "OffsetCriteria{" + "_offsetType=" + _offsetType + ", _offsetString='" + _offsetString + '\'' + '}';
  }
}


text:
@interface NewerGuideViewController : UIViewController

@end