Best JavaScript code snippet using wpt
sand.js
Source:sand.js
1const elements = {2 void: {3 red: 0, green: 0, blue: 0,4 density: 0, gravity: 0, slip: 0, slide: 0, scatter: 0,5 reactions: [],6 selfReactions: [],7 },8 wall: {9 red: 0.5, green: 0.5, blue: 0.5,10 density: 1, gravity: 0, slip: 0, slide: 0, scatter: 0,11 immobile: true,12 reactions: [],13 selfReactions: [],14 },15 sand: {16 red: 1.0, green: 0.8, blue: 0.2,17 density: 0.7, gravity: 0.8, slip: 0, slide: 0.8, scatter: 0,18 reactions: [],19 selfReactions: [],20 },21 salt: {22 red: 0.8, green: 0.8, blue: 0.8,23 density: 0.6, gravity: 0.75, slip: 0.05, slide: 0.7, scatter: 0,24 reactions: [],25 selfReactions: [],26 },27 water: {28 red: 0, green: 0.4, blue: 1,29 density: 0.5, gravity: 0.8, slip: 0.95, slide: 0, scatter: 0.35,30 reactions: [],31 selfReactions: [],32 },33 plant: {34 red: 0.2, green: 0.8, blue: 0,35 density: 1, gravity: 0, slip: 0, slide: 0, scatter: 0,36 immobile: true,37 reactions: [],38 selfReactions: [],39 },40 fire: {41 red: 0.9, green: 0.2, blue: 0.1,42 density: -0.5, gravity: -0.2, slip: 0, slide: 0, scatter: 0.8,43 reactions: [],44 selfReactions: [],45 },46 oil: {47 red: 0.6, green: 0.4, blue: 0.15,48 density: 0.4, gravity: 0.75, slip: 0.75, slide: 0, scatter: 0.2,49 reactions: [],50 selfReactions: [],51 },52 nitro: {53 red: 0.1, green: 0.5, blue: 0,54 density: 0.3, gravity: 0.9, slip: 0.5, slide: 0, scatter: 0,55 reactions: [],56 selfReactions: [],57 },58 spout: {59 red: 0.5, green: 0.7, blue: 1.0,60 density: 1, gravity: 0, slip: 0, slide: 0, scatter: 0,61 immobile: true,62 reactions: [],63 selfReactions: [],64 },65 torch: {66 red: 0.4, green: 0.1, blue: 0,67 density: 1, gravity: 0, slip: 0, slide: 0, scatter: 0,68 immobile: true,69 reactions: [],70 selfReactions: [],71 },72 well: {73 red: 0.5, green: 0.3, blue: 0,74 density: 1, gravity: 0, slip: 0, slide: 0, scatter: 0,75 immobile: true,76 reactions: [],77 selfReactions: [],78 },79 saltwater: {80 red: 0.1, green: 0.45, blue: 1,81 density: 0.51, gravity: 0.8, slip: 0.95, slide: 0, scatter: 0.25,82 hidden: true,83 reactions: [],84 selfReactions: [],85 },86 steam: {87 red: 0.3, green: 0.65, blue: 0.8,88 density: -0.2, gravity: -0.2, slip: 0, slide: 0, scatter: 0.4,89 hidden: true,90 reactions: [],91 selfReactions: [],92 },93 glass: {94 red: 0.9, green: 0.92, blue: 0.92,95 density: 1, gravity: 0, slip: 0, slide: 0, scatter: 0,96 immobile: true,97 hidden: true,98 reactions: [],99 selfReactions: [],100 },101};102let elementId = 0;103for(const elementName in elements){104 elements[elementName].id = elementId++;105}106elements.fire.selfReactions.push({107 chance: 0.2,108 becomes: elements.void,109});110elements.fire.reactions.push({111 chance: 0.75,112 becomes: elements.void,113 singleNeighbor: {114 element: elements.void,115 becomes: elements.fire,116 minimum: 1,117 maximum: 6,118 affects: 1,119 },120});121elements.fire.reactions.push({122 chance: 0.85,123 becomes: elements.void,124 singleNeighbor: {125 element: elements.water,126 becomes: elements.steam,127 minimum: 1,128 maximum: 8,129 affects: 4,130 },131});132elements.fire.reactions.push({133 chance: 0.85,134 becomes: elements.fire,135 singleNeighbor: {136 element: elements.plant,137 becomes: elements.fire,138 minimum: 1,139 maximum: 8,140 affects: 4,141 },142});143elements.fire.reactions.push({144 chance: 0.98,145 becomes: elements.fire,146 singleNeighbor: {147 element: elements.oil,148 becomes: elements.fire,149 minimum: 1,150 maximum: 8,151 affects: 5,152 },153});154elements.fire.reactions.push({155 chance: 0.25,156 explosive: 2,157 becomes: elements.fire,158 singleNeighbor: {159 element: elements.nitro,160 becomes: elements.fire,161 minimum: 1,162 maximum: 8,163 affects: 1,164 },165});166elements.fire.reactions.push({167 chance: 0.9,168 explosive: 2,169 becomes: elements.fire,170 singleNeighbor: {171 element: elements.nitro,172 becomes: elements.fire,173 minimum: 1,174 maximum: 8,175 affects: 2,176 },177});178elements.sand.reactions.push({179 chance: 0.45,180 becomes: elements.glass,181 singleNeighbor: {182 element: elements.fire,183 becomes: elements.fire,184 minimum: 2,185 maximum: 8,186 affects: 0,187 },188});189elements.plant.reactions.push({190 chance: 0.007,191 becomes: elements.plant,192 singleNeighbor: {193 element: elements.water,194 becomes: elements.plant,195 minimum: 1,196 maximum: 8,197 affects: 2,198 },199});200elements.plant.reactions.push({201 chance: 0.025,202 becomes: elements.plant,203 singleNeighbor: {204 element: elements.water,205 becomes: elements.void,206 minimum: 1,207 maximum: 8,208 affects: 2,209 },210});211elements.plant.reactions.push({212 chance: 0.2,213 becomes: elements.plant,214 multiNeighbor: [215 {216 element: elements.water,217 becomes: elements.plant,218 minimum: 1,219 maximum: 8,220 affects: 1,221 },222 {223 element: elements.plant,224 becomes: elements.plant,225 minimum: 0,226 maximum: 2,227 affects: 0,228 },229 ],230});231elements.plant.reactions.push({232 chance: 0.2,233 becomes: elements.plant,234 multiNeighbor: [235 {236 element: elements.water,237 becomes: elements.plant,238 minimum: 1,239 maximum: 8,240 affects: 1,241 },242 {243 element: elements.plant,244 becomes: elements.plant,245 minimum: 0,246 maximum: 4,247 affects: 0,248 },249 ],250});251elements.water.reactions.push({252 chance: 0.95,253 becomes: elements.saltwater,254 singleNeighbor: {255 element: elements.salt,256 becomes: elements.void,257 minimum: 1,258 maximum: 8,259 affects: 1,260 },261});262elements.saltwater.reactions.push({263 chance: 0.85,264 becomes: elements.salt,265 singleNeighbor: {266 element: elements.fire,267 becomes: elements.steam,268 minimum: 1,269 maximum: 8,270 affects: 1,271 },272});273elements.steam.selfReactions.push({274 chance: 0.008,275 becomes: elements.water,276});277elements.steam.reactions.push({278 chance: 0.8,279 becomes: elements.water,280 singleNeighbor: {281 element: elements.steam,282 becomes: elements.water,283 minimum: 3,284 maximum: 8,285 affects: 2,286 },287});288elements.steam.reactions.push({289 chance: 0.05,290 becomes: elements.void,291 singleNeighbor: {292 element: elements.void,293 becomes: elements.steam,294 minimum: 1,295 maximum: 8,296 affects: 1,297 },298});299elements.torch.reactions.push({300 chance: 0.75,301 becomes: elements.torch,302 singleNeighbor: {303 element: elements.void,304 becomes: elements.fire,305 minimum: 1,306 maximum: 8,307 affects: 2,308 },309});310elements.torch.reactions.push({311 chance: 0.4,312 becomes: elements.torch,313 singleNeighbor: {314 element: elements.water,315 becomes: elements.steam,316 minimum: 1,317 maximum: 8,318 affects: 1,319 },320});321elements.torch.reactions.push({322 chance: 0.2,323 becomes: elements.torch,324 singleNeighbor: {325 element: elements.oil,326 becomes: elements.fire,327 minimum: 1,328 maximum: 8,329 affects: 2,330 },331});332elements.torch.reactions.push({333 chance: 0.4,334 becomes: elements.torch,335 singleNeighbor: {336 element: elements.nitro,337 becomes: elements.fire,338 minimum: 1,339 maximum: 8,340 affects: 3,341 },342});343elements.spout.reactions.push({344 chance: 0.12,345 becomes: elements.spout,346 singleNeighbor: {347 element: elements.void,348 becomes: elements.water,349 minimum: 1,350 maximum: 8,351 affects: 1,352 },353});354elements.well.reactions.push({355 chance: 0.13,356 becomes: elements.well,357 singleNeighbor: {358 element: elements.void,359 becomes: elements.oil,360 minimum: 1,361 maximum: 7,362 affects: 1,363 },364});365const defaultElement = elements.void;366function shuffleArray(array){367 const newArray = [];368 let i = 0;369 for(const element of array){370 let j = Math.floor(Math.random() * i);371 if(i === j){372 newArray.push(element);373 }else{374 newArray.push(newArray[j]);375 newArray[j] = element;376 }377 i++;378 }379 return newArray;380}381function rgb2hex(red, green, blue){382 return "#" + (383 ("0" + Math.floor(red * 255).toString(16)).slice(-2) +384 ("0" + Math.floor(green * 255).toString(16)).slice(-2) +385 ("0" + Math.floor(blue * 255).toString(16)).slice(-2)386 );387}388function nextPow2(n){389 return Math.pow(2, Math.ceil(Math.log2(n)));390}391function newBuffer(width, height, fill){392 const buffer = [];393 buffer.width = width;394 buffer.height = height;395 for(let i = 0; i < width * height; i++){396 buffer.push(fill);397 }398 return buffer;399}400function newTexture(gl, pixels){401 return twgl.createTexture(gl, {402 target: gl.TEXTURE_2D,403 width: pixels.width,404 height: pixels.height,405 minMag: gl.NEAREST,406 internalFormat: gl.RGB,407 format: gl.RGB,408 wrap: gl.CLAMP_TO_EDGE,409 src: pixels,410 });411}412function newPixelData(buffer){413 const textureWidth = nextPow2(buffer.width);414 const textureHeight = nextPow2(buffer.height);415 const pixels = new Uint8Array(textureWidth * textureHeight * 3);416 pixels.width = textureWidth;417 pixels.height = textureHeight;418 return blitPixelData(pixels, buffer);419}420function blitPixelData(pixels, buffer){421 let j = 0;422 for(let i = 0; i < buffer.length; i++){423 pixels[j++] = Math.floor(buffer[i].red * 255);424 pixels[j++] = Math.floor(buffer[i].green * 255);425 pixels[j++] = Math.floor(buffer[i].blue * 255);426 if(i % buffer.width === buffer.width - 1){427 j += (pixels.width - buffer.width) * 3;428 }429 }430 return pixels;431}432function updateBuffers(timestamp, timeBuffer, readBuffer, writeBuffer){433 const allNeighbors = [434 -readBuffer.width - 1,435 -readBuffer.width,436 -readBuffer.width + 1,437 -1,438 +1,439 +readBuffer.width - 1,440 +readBuffer.width,441 +readBuffer.width + 1,442 ];443 const westEdgeNeighbors = [444 -readBuffer.width,445 -readBuffer.width + 1,446 +1,447 +readBuffer.width,448 +readBuffer.width + 1,449 ];450 const eastEdgeNeighbors = [451 -readBuffer.width - 1,452 -readBuffer.width,453 -1,454 +readBuffer.width - 1,455 +readBuffer.width,456 ];457 allNeighbors.randomOrder = [458 shuffleArray(allNeighbors),459 shuffleArray(allNeighbors),460 shuffleArray(allNeighbors),461 shuffleArray(allNeighbors),462 shuffleArray(allNeighbors),463 shuffleArray(allNeighbors),464 ];465 westEdgeNeighbors.randomOrder = [466 shuffleArray(westEdgeNeighbors),467 shuffleArray(westEdgeNeighbors),468 shuffleArray(westEdgeNeighbors),469 shuffleArray(westEdgeNeighbors),470 shuffleArray(westEdgeNeighbors),471 shuffleArray(westEdgeNeighbors),472 ];473 eastEdgeNeighbors.randomOrder = [474 shuffleArray(eastEdgeNeighbors),475 shuffleArray(eastEdgeNeighbors),476 shuffleArray(eastEdgeNeighbors),477 shuffleArray(eastEdgeNeighbors),478 shuffleArray(eastEdgeNeighbors),479 shuffleArray(eastEdgeNeighbors),480 ];481 let neighborOrderIndex = 0;482 const mode = timestamp % 2;483 const increment = mode ? -1 : +1;484 const totalCells = readBuffer.width * readBuffer.height;485 let i = mode * (totalCells - 1);486 let west = i - 1;487 let east = i + 1;488 let north = i - readBuffer.width;489 let south = i + readBuffer.width;490 // for(let j = 0; j < readBuffer.length; j++){491 // writeBuffer[j] = readBuffer[j];492 // }493 while(i >= 0 && i < totalCells){494 let random = Math.random();495 if(timeBuffer[i] === timestamp){496 i += increment;497 north += increment;498 south += increment;499 east += increment;500 west += increment;501 continue;502 };503 const cell = readBuffer[i];504 const northwest = north - 1;505 const northeast = north + 1;506 const southwest = south - 1;507 const southeast = south + 1;508 const inRow = i % readBuffer.width;509 const westEdge = inRow === 0;510 const eastEdge = inRow === readBuffer.width - 1;511 const slipRandom = Math.random();512 if(cell.gravity > 0 && south < totalCells &&513 !readBuffer[south].immobile &&514 cell.density > readBuffer[south].density515 ){516 if(timeBuffer[south] < timestamp && cell.gravity > random){517 writeBuffer[i] = readBuffer[south];518 writeBuffer[south] = cell;519 timeBuffer[i] = timestamp;520 timeBuffer[south] = timestamp;521 }522 random = Math.random();523 }else if(cell.gravity < 0 && north >= 0 &&524 !readBuffer[north].immobile &&525 cell.density < readBuffer[north].density526 ){527 if(timeBuffer[north] < timestamp && cell.gravity < -random){528 writeBuffer[i] = readBuffer[north];529 writeBuffer[north] = cell;530 timeBuffer[i] = timestamp;531 timeBuffer[north] = timestamp;532 }533 random = Math.random();534 }else if(cell.gravity && cell.slide > random){535 const westStep = cell.gravity > 0 ? southwest : northwest;536 const eastStep = cell.gravity > 0 ? southeast : northeast;537 const westAllowed = !westEdge && (538 westStep >= 0 && westStep < totalCells &&539 !readBuffer[westStep].immobile && (cell.gravity > 0 ?540 cell.density > readBuffer[westStep].density :541 cell.density < readBuffer[westStep].density542 )543 );544 const eastAllowed = !eastEdge && (545 eastStep >= 0 && eastStep < totalCells &&546 !readBuffer[eastStep].immobile && (cell.gravity > 0 ?547 cell.density > readBuffer[eastStep].density :548 cell.density < readBuffer[eastStep].density549 )550 );551 if(westAllowed && (!eastAllowed || Math.random() < 0.5)){552 writeBuffer[i] = readBuffer[westStep];553 writeBuffer[westStep] = cell;554 timeBuffer[i] = timestamp;555 timeBuffer[westStep] = timestamp;556 }else if(eastAllowed){557 writeBuffer[i] = readBuffer[eastStep];558 writeBuffer[eastStep] = cell;559 timeBuffer[i] = timestamp;560 timeBuffer[eastStep] = timestamp;561 }562 random = Math.random();563 }else if(cell.slip > slipRandom && (564 (cell.gravity > 0 && south < totalCells && timeBuffer[south] < timestamp - 2) ||565 (cell.gravity < 0 && north >= 0 && timeBuffer[north] < timestamp - 2)566 )){567 const westBound = i - (i % readBuffer.width);568 const eastBound = westBound + readBuffer.width - 1;569 let westCell = i;570 let eastCell = i;571 let westStep = cell.gravity > 0 ? south : north;572 let eastStep = westStep;573 let westUnder = westStep + readBuffer.width;574 let eastUnder = westUnder;575 let westOpen = false;576 let eastOpen = false;577 let lastWest = 0;578 let lastEast = 0;579 if(cell.gravity > 0){580 while(westCell > westBound && westStep < totalCells - 1){581 westCell--; westStep--; westUnder--;582 if(583 readBuffer[westCell].immobile ||584 cell.density < readBuffer[westCell].density585 ) break;586 if(587 !readBuffer[westStep].immobile &&588 cell.density > readBuffer[westStep].density589 ){590 if(timeBuffer[westStep] < timestamp){591 westOpen = true;592 lastWest = westStep;593 }594 if(Math.random() < 0.25 || (595 westUnder < totalCells &&596 !readBuffer[westUnder].immobile && 597 cell.density > readBuffer[westUnder].density598 )) break;599 }600 }601 while(eastCell < eastBound && eastStep < totalCells - 1){602 eastCell++; eastStep++; eastUnder++;603 if(604 readBuffer[eastCell].immobile ||605 cell.density < readBuffer[eastCell].density606 ) break;607 if(608 !readBuffer[eastStep].immobile &&609 cell.density > readBuffer[eastStep].density610 ){611 if(timeBuffer[eastStep] < timestamp){612 eastOpen = true;613 lastEast = eastStep;614 }615 if(Math.random() < 0.25 || (616 eastUnder < totalCells &&617 !readBuffer[eastUnder].immobile && 618 cell.density > readBuffer[eastUnder].density619 )) break;620 }621 }622 }else{623 // TODO624 }625 let swapTarget;626 if(westOpen && eastOpen){627 swapTarget = Math.random() < 0.5 ? lastWest : lastEast;628 }else if(westOpen){629 swapTarget = lastWest;630 }else if(eastOpen){631 swapTarget = lastEast;632 }633 if(swapTarget !== undefined){634 writeBuffer[i] = readBuffer[swapTarget];635 writeBuffer[swapTarget] = cell;636 timeBuffer[i] = timestamp;637 timeBuffer[swapTarget] = timestamp;638 }639 random = Math.random();640 }else if(cell.scatter > random){641 const westAllowed = !westEdge && (642 timeBuffer[west] < timestamp &&643 !readBuffer[west].immobile && (cell.gravity > 0 ?644 cell.density >= readBuffer[west].density :645 cell.density <= readBuffer[west].density646 )647 );648 const eastAllowed = !eastEdge && (649 timeBuffer[east] < timestamp &&650 !readBuffer[east].immobile && (cell.gravity > 0 ?651 cell.density >= readBuffer[east].density :652 cell.density <= readBuffer[east].density653 )654 );655 if(westAllowed && (!eastAllowed || Math.random() < 0.5)){656 writeBuffer[i] = readBuffer[west];657 writeBuffer[west] = cell;658 timeBuffer[i] = timestamp;659 timeBuffer[west] = timestamp;660 }else if(eastAllowed){661 writeBuffer[i] = readBuffer[east];662 writeBuffer[east] = cell;663 timeBuffer[i] = timestamp;664 timeBuffer[east] = timestamp;665 }666 random = Math.random();667 }668 if(timeBuffer[i] < timestamp){669 writeBuffer[i] = readBuffer[i];670 if(cell.reactions.length){671 const neighbors = [];672 if(north >= 0) neighbors[readBuffer[north].id] = (673 (neighbors[readBuffer[north].id] || 0) + 1674 );675 if(south < totalCells) neighbors[readBuffer[south].id] = (676 (neighbors[readBuffer[south].id] || 0) + 1677 );678 if(!westEdge){679 if(northwest >= 0) neighbors[readBuffer[northwest].id] = (680 (neighbors[readBuffer[northwest].id] || 0) + 1681 );682 if(west >= 0) neighbors[readBuffer[west].id] = (683 (neighbors[readBuffer[west].id] || 0) + 1684 );685 if(southwest < totalCells) neighbors[readBuffer[southwest].id] = (686 (neighbors[readBuffer[southwest].id] || 0) + 1687 );688 }689 if(!eastEdge){690 if(northeast >= 0) neighbors[readBuffer[northeast].id] = (691 (neighbors[readBuffer[northeast].id] || 0) + 1692 );693 if(southeast < totalCells) neighbors[readBuffer[southeast].id] = (694 (neighbors[readBuffer[southeast].id] || 0) + 1695 );696 if(east < totalCells) neighbors[readBuffer[east].id] = (697 (neighbors[readBuffer[east].id] || 0) + 1698 );699 }700 for(const reaction of cell.reactions){701 if(reaction.singleNeighbor && reaction.chance > random){702 const actual = neighbors[reaction.singleNeighbor.element.id];703 if(704 actual >= reaction.singleNeighbor.minimum &&705 actual <= reaction.singleNeighbor.maximum706 ){707 writeBuffer[i] = reaction.becomes;708 timeBuffer[i] = timestamp;709 if(reaction.explosive){710 let expWest = west;711 let expEast = east;712 let expNorth = north;713 let expSouth = south;714 for(let exp = 0; exp <= reaction.explosive; exp++){715 if(expWest >= 0 && !westEdge){716 writeBuffer[expWest] = reaction.becomes;717 timeBuffer[expWest] = timestamp;718 if(expWest % readBuffer.width === 0) break;719 expWest--;720 }721 if(expEast < totalCells && !eastEdge){722 writeBuffer[expEast] = reaction.becomes;723 timeBuffer[expEast] = timestamp;724 if(expEast % readBuffer.width === readBuffer.width - 1) break;725 expEast++;726 }727 if(expNorth >= 0){728 writeBuffer[expNorth] = reaction.becomes;729 timeBuffer[expNorth] = timestamp;730 expNorth -= readBuffer.width;731 }732 if(expSouth < totalCells){733 writeBuffer[expSouth] = reaction.becomes;734 timeBuffer[expSouth] = timestamp;735 expSouth += readBuffer.height;736 }737 }738 }else{739 const whichNeighbors = (westEdge ?740 westEdgeNeighbors : (eastEdge ? eastEdgeNeighbors : allNeighbors)741 );742 const neighborIndexes = whichNeighbors.randomOrder[neighborOrderIndex++];743 if(neighborOrderIndex >= whichNeighbors.randomOrder.length){744 neighborOrderIndex = 0;745 }746 let changed = 0;747 for(let j = 0; j < 8 && changed < reaction.singleNeighbor.affects; j++){748 const index = i + neighborIndexes[j];749 if(750 index >= 0 && index < readBuffer.length &&751 readBuffer[index] === reaction.singleNeighbor.element752 ){753 writeBuffer[index] = reaction.singleNeighbor.becomes;754 timeBuffer[index] = timestamp;755 changed++;756 }757 }758 break;759 }760 }761 }else if(reaction.multiNeighbor && reaction.chance > random){762 let match = true;763 for(const thisNeighbor of reaction.multiNeighbor){764 const actual = neighbors[thisNeighbor.element.id];765 if(766 actual < thisNeighbor.minimum ||767 actual > thisNeighbor.maximum768 ){769 match = false;770 break;771 }772 }773 if(match) for(const thisNeighbor of reaction.multiNeighbor){774 writeBuffer[i] = reaction.becomes;775 timeBuffer[i] = timestamp;776 const whichNeighbors = (westEdge ?777 westEdgeNeighbors : (eastEdge ? eastEdgeNeighbors : allNeighbors)778 );779 const neighborIndexes = whichNeighbors.randomOrder[neighborOrderIndex++];780 if(neighborOrderIndex >= whichNeighbors.randomOrder.length){781 neighborOrderIndex = 0;782 }783 let changed = 0;784 for(let j = 0; j < 8 && changed < thisNeighbor.affects; j++){785 const index = i + neighborIndexes[j];786 if(787 index >= 0 && index < readBuffer.length &&788 readBuffer[index] === thisNeighbor.element789 ){790 writeBuffer[index] = thisNeighbor.becomes;791 timeBuffer[index] = timestamp;792 changed++;793 }794 }795 break;796 }797 }798 random = Math.random()799 }800 }801 if(cell.selfReactions.length && timeBuffer[i] < timestamp){802 for(const reaction of cell.selfReactions){803 if(reaction.chance > random){804 writeBuffer[i] = reaction.becomes;805 timeBuffer[i] = timestamp;806 }807 random = Math.random();808 }809 }810 }811 i += increment;812 north += increment;813 south += increment;814 east += increment;815 west += increment;816 }817}818const vertexShader = `819 precision mediump float;820 attribute vec2 a_position;821 varying vec2 v_position;822 void main(){823 v_position = a_position;824 gl_Position = vec4(825 a_position.x * 2.0 - 1.0, v_position.y * -2.0 + 1.0, 0.0, 1.0826 );827 }828`;829const fragmentShader = `830 precision mediump float;831 uniform sampler2D u_texture;832 varying vec2 v_position;833 void main(){834 gl_FragColor = texture2D(u_texture, v_position);835 }836`;837function begin(){838 const canvas = document.getElementById("glCanvas");839 canvas.width = canvas.clientWidth;840 canvas.height = canvas.clientHeight;841 const gl = twgl.getWebGLContext(canvas, {842 alpha: false,843 antialias: false,844 depth: false,845 stencil: false,846 });847 twgl.setDefaults({848 textureColor: [0, 0, 0, 1],849 attribPrefix: "a_",850 });851 852 gl.enable(gl.CULL_FACE);853 gl.clearColor(0, 0, 0, 1);854 855 const bufferWidth = 128;856 const bufferHeight = 128;857 858 let currentBuffer = newBuffer(bufferWidth, bufferHeight, defaultElement);859 let reserveBuffer = newBuffer(bufferWidth, bufferHeight, defaultElement);860 const timeBuffer = newBuffer(bufferWidth, bufferHeight, 0);861 862 const pixels = newPixelData(currentBuffer);863 const texture = newTexture(gl, pixels);864 865 const programInfo = twgl.createProgramInfo(gl, [866 vertexShader, fragmentShader,867 ]);868 gl.useProgram(programInfo.program);869 twgl.setUniforms(programInfo, {870 u_texture: texture,871 });872 873 const bufferInfo = twgl.createBufferInfoFromArrays(gl, {874 position: {875 numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1]876 },877 });878 879 let frame = 1;880 881 // http://stackoverflow.com/questions/381795/how-to-disable-right-click-context-menu-in-javascript882 canvas.addEventListener("contextmenu", function(event){883 if(event.preventDefault) event.preventDefault();884 if(event.stopPropagation) event.stopPropagation();885 return false;886 });887 888 let drawing = false;889 let selectedElement = elements.wall;890 891 let lastMouseEvent = undefined;892 function drawAtMouse(event){893 const rect = canvas.getBoundingClientRect();894 const x = (event.clientX - rect.left) / rect.width;895 const y = (event.clientY - rect.top) / rect.height;896 if(x >= 0 && y >= 0 && x < 1 && y < 1){897 const i = Math.floor(x * bufferWidth);898 const j = Math.floor(y * bufferHeight);899 for(let brushX = -2; brushX < 2; brushX++){900 for(let brushY = -2; brushY < 2; brushY++){901 const drawX = i + brushX;902 const drawY = j + brushY;903 if(drawX >= 0 && drawY >= 0 && drawX < bufferWidth && drawY < bufferHeight){904 currentBuffer[drawX + drawY * bufferWidth] = selectedElement;905 }906 }907 }908 }909 }910 911 canvas.addEventListener("mousedown", function(event){912 drawing = true;913 lastMouseEvent = event;914 }, false);915 canvas.addEventListener("mousemove", function(event){916 lastMouseEvent = event;917 }, false);918 canvas.addEventListener("mouseup", function(event){919 drawing = false;920 }, false);921 canvas.addEventListener("mouseout", function(event){922 drawing = false;923 }, false);924 925 const elementButtons = [];926 for(const elementName in elements){927 const element = elements[elementName];928 if(!element.hidden){929 const button = document.createElement("div");930 elementButtons.push(button);931 const label = document.createTextNode(elementName);932 button.appendChild(label);933 const hex = rgb2hex(element.red * 0.5, element.green * 0.5, element.blue * 0.5);934 button.classList.add("elementButton");935 button.style = `background-color: ${hex};`;936 document.getElementById("elementButtons").appendChild(button);937 button.onclick = function(event){938 selectedElement = element;939 for(const other of elementButtons){940 other.classList.remove("elementButtonSelected");941 }942 button.classList.add("elementButtonSelected");943 }944 }945 }946 947 const update = () => {948 // Draw to buffer949 if(drawing) drawAtMouse(lastMouseEvent);950 // Update game state951 updateBuffers(frame, timeBuffer, currentBuffer, reserveBuffer)952 const tempBuffer = currentBuffer;953 currentBuffer = reserveBuffer;954 reserveBuffer = tempBuffer;955 // Update texture956 blitPixelData(pixels, currentBuffer);957 gl.bindTexture(gl.TEXTURE_2D, texture);958 gl.texSubImage2D(959 gl.TEXTURE_2D, 0, 0, 0, bufferWidth, bufferHeight,960 gl.RGB, gl.UNSIGNED_BYTE, pixels961 );962 // Render963 twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);964 twgl.drawBufferInfo(gl, bufferInfo, gl.TRIANGLE_STRIP);965 // Ready for next frame966 requestAnimationFrame(update);967 frame++;968 };969 970 requestAnimationFrame(update);971}...
sandboxed_FileSystemSyncAccessHandle-read-write.https.tentative.worker.js
Source:sandboxed_FileSystemSyncAccessHandle-read-write.https.tentative.worker.js
1importScripts("/resources/testharness.js");2importScripts('resources/sync-access-handle-test.js');3'use strict';4sync_access_handle_test((t, handle) => {5 const readBuffer = new Uint8Array(24);6 const readBytes = handle.read(readBuffer, {at: 0});7 assert_equals(0, readBytes, 'Check that no bytes were read');8}, 'Test reading an empty file through a sync access handle.');9sync_access_handle_test((t, handle) => {10 if (!('TextEncoder' in self)) {11 return;12 }13 const encoder = new TextEncoder();14 const decoder = new TextDecoder();15 const text = 'Hello Storage Foundation';16 const writeBuffer = new TextEncoder().encode(text);17 const writtenBytes = handle.write(writeBuffer, {at: 0});18 assert_equals(19 writeBuffer.byteLength, writtenBytes,20 'Check that all bytes were written.');21 let readBuffer = new Uint8Array(writtenBytes);22 let readBytes = handle.read(readBuffer, {at: 0});23 assert_equals(writtenBytes, readBytes, 'Check that all bytes were read');24 assert_equals(25 text, new TextDecoder().decode(readBuffer),26 'Check that the written bytes and the read bytes match');27 // Test a read of less bytes than available.28 const expected = 'Storage';29 readBuffer = new Uint8Array(expected.length);30 readBytes = handle.read(readBuffer, {at: text.indexOf(expected)});31 assert_equals(readBuffer.length, readBytes, 'Check that all bytes were read');32 const actual = new TextDecoder().decode(readBuffer);33 assert_equals(34 expected, actual,35 'Partial read returned unexpected contents');36}, 'Test writing and reading through a sync access handle.');37sync_access_handle_test((t, handle) => {38 if (!('TextEncoder' in self)) {39 return;40 }41 const encoder = new TextEncoder();42 const decoder = new TextDecoder();43 for (text of ['Hello', 'Longer Text']) {44 const writeBuffer = new TextEncoder().encode(text);45 const writtenBytes = handle.write(writeBuffer, {at: 0});46 assert_equals(47 writeBuffer.byteLength, writtenBytes,48 'Check that all bytes were written.');49 const readBuffer = new Uint8Array(writtenBytes);50 const readBytes = handle.read(readBuffer, {at: 0});51 assert_equals(writtenBytes, readBytes, 'Check that all bytes were read');52 assert_equals(53 text, new TextDecoder().decode(readBuffer),54 'Check that the written bytes and the read bytes match');55 }56}, 'Test second write that is bigger than the first write');57sync_access_handle_test((t, handle) => {58 if (!('TextEncoder' in self)) {59 return;60 }61 const encoder = new TextEncoder();62 const decoder = new TextDecoder();63 for (tuple64 of [{input: 'Hello World', expected: 'Hello World'},65 {input: 'foobar', expected: 'foobarWorld'}]) {66 const text = tuple.input;67 const expected = tuple.expected;68 const writeBuffer = new TextEncoder().encode(text);69 const writtenBytes = handle.write(writeBuffer, {at: 0});70 assert_equals(71 writeBuffer.byteLength, writtenBytes,72 'Check that all bytes were written.');73 const readBuffer = new Uint8Array(expected.length);74 const readBytes = handle.read(readBuffer, {at: 0});75 assert_equals(expected.length, readBytes, 'Check that all bytes were read');76 assert_equals(77 expected, new TextDecoder().decode(readBuffer),78 'Check that the written bytes and the read bytes match');79 }80}, 'Test second write that is smaller than the first write');81sync_access_handle_test((t, handle) => {82 const expected = 17;83 const writeBuffer = new Uint8Array(1);84 writeBuffer[0] = expected;85 const offset = 5;86 const writtenBytes = handle.write(writeBuffer, {at: offset});87 assert_equals(88 writeBuffer.byteLength, writtenBytes,89 'Check that all bytes were written.');90 const fileLength = writeBuffer.byteLength + offset;91 const readBuffer = new Uint8Array(fileLength);92 const readBytes = handle.read(readBuffer, {at: 0});93 assert_equals(fileLength, readBytes, 'Check that all bytes were read');94 for (let i = 0; i < offset; ++i) {95 assert_equals(96 readBuffer[i], 0,97 `Gaps in the file should be filled with 0, but got ${readBuffer[i]}.`);98 }99 assert_equals(100 readBuffer[offset], expected,101 'Gaps in the file should be filled with 0.');102}, 'Test initial write with an offset');103sync_access_handle_test((t, handle) => {104 if (!('TextEncoder' in self)) {105 return;106 }107 const encoder = new TextEncoder();108 const decoder = new TextDecoder();109 for (tuple110 of [{input: 'Hello World', expected: 'Hello World', offset: 0},111 {input: 'foobar', expected: 'Hello foobar', offset: 6}]) {112 const text = tuple.input;113 const expected = tuple.expected;114 const offset = tuple.offset;115 const writeBuffer = new TextEncoder().encode(text);116 const writtenBytes = handle.write(writeBuffer, {at: offset});117 assert_equals(118 writeBuffer.byteLength, writtenBytes,119 'Check that all bytes were written.');120 const readBuffer = new Uint8Array(expected.length);121 const readBytes = handle.read(readBuffer, {at: 0});122 assert_equals(expected.length, readBytes, 'Check that all bytes were read');123 const actual = new TextDecoder().decode(readBuffer);124 assert_equals(125 expected, actual,126 'Check content read from the handle');127 }128}, 'Test overwriting the file at an offset');129sync_access_handle_test((t, handle) => {130 if (!('TextEncoder' in self)) {131 return;132 }133 const encoder = new TextEncoder();134 const decoder = new TextDecoder();135 const text = 'Hello Storage Foundation';136 const writeBuffer = new TextEncoder().encode(text);137 const writtenBytes = handle.write(writeBuffer, {at: 0});138 assert_equals(139 writeBuffer.byteLength, writtenBytes,140 'Check that all bytes were written.');141 const bufferLength = text.length;142 for (tuple143 of [{offset: 0, expected: text},144 {offset: 6, expected: text.substring(6)}]) {145 const offset = tuple.offset;146 const expected = tuple.expected;147 const readBuffer = new Uint8Array(bufferLength);148 const readBytes = handle.read(readBuffer, {at: offset});149 assert_equals(expected.length, readBytes, 'Check that all bytes were read');150 const actual = new TextDecoder().decode(readBuffer);151 assert_true(152 actual.startsWith(expected),153 `Expected to read ${expected} but the actual value was ${actual}.`);154 }155 const readBuffer = new Uint8Array(bufferLength);156 // Offset is greater than the file length.157 const readBytes = handle.read(readBuffer, {at: bufferLength + 1});158 assert_equals(0, readBytes, 'Check that no bytes were read');159 for (let i = 0; i < readBuffer.byteLength; ++i) {160 assert_equals(0, readBuffer[i], 'Check that the read buffer is unchanged.');161 }162}, 'Test read at an offset');163sync_access_handle_test((t, handle) => {164 const readBuffer = new Uint8Array(24);165 assert_throws_dom(166 'NotSupportedError', () => handle.read(readBuffer, { at: -1 }));167}, 'Test reading at a negative offset fails.');...
unix-read.test.ts
Source:unix-read.test.ts
1import { BindingsError } from '.'2import { assert, shouldReject } from '../test/assert'3import { LinuxPortBinding } from './linux'4import { unixRead } from './unix-read'5const makeFsRead =6 (bytesRead: number, fill: number): any =>7 (fd: number, buffer: Buffer, offset: number, length: number) => {8 buffer.fill(fill, offset, Math.min(length, bytesRead))9 return {10 buffer,11 bytesRead,12 }13 }14const makeFsReadError = (code: string) => {15 const err = new Error(`Error: ${code}`)16 ;(err as any).code = code17 return () => {18 throw err19 }20}21// eslint-disable-next-line @typescript-eslint/ban-types22const sequenceCalls = (...functions: Function[]) => {23 const funcs = [...functions]24 return (...args: any[]) => {25 const func = funcs.shift()26 if (func) {27 return func(...args)28 } else {29 throw new Error('"sequenceCalls" has no more functions')30 }31 }32}33const makeMockBinding = (): LinuxPortBinding => {34 return {35 isOpen: true,36 fd: 1,37 poller: {38 once(event: any, func: () => void) {39 setImmediate(func)40 },41 },42 } as any43}44describe('unixRead', () => {45 let mock: LinuxPortBinding46 beforeEach(() => {47 mock = makeMockBinding()48 })49 it('rejects when not open', async () => {50 (mock as any).isOpen = false51 const readBuffer = Buffer.alloc(8, 0)52 await shouldReject(unixRead({ binding: mock, buffer: readBuffer, offset: 0, length: 8, fsReadAsync: makeFsRead(8, 255) }))53 })54 it('handles reading the requested number of bytes', async () => {55 const readBuffer = Buffer.alloc(8, 0)56 const { bytesRead, buffer } = await unixRead({ binding: mock, buffer: readBuffer, offset: 0, length: 8, fsReadAsync: makeFsRead(8, 255) })57 assert.strictEqual(bytesRead, 8)58 assert.strictEqual(buffer, readBuffer)59 assert.deepStrictEqual(buffer, Buffer.alloc(8, 255))60 })61 it('handles reading less than requested number of bytes', async () => {62 const readBuffer = Buffer.alloc(8, 0)63 const { bytesRead, buffer } = await unixRead({ binding: mock, buffer: readBuffer, offset: 0, length: 8, fsReadAsync: makeFsRead(4, 255) })64 assert.strictEqual(bytesRead, 4)65 assert.strictEqual(buffer, readBuffer)66 assert.deepStrictEqual(buffer, Buffer.from([255, 255, 255, 255, 0, 0, 0, 0]))67 })68 it('handles reading 0 bytes then requested number of bytes', async () => {69 const readBuffer = Buffer.alloc(8, 0)70 const fsReadAsync = sequenceCalls(makeFsRead(0, 0), makeFsRead(8, 255))71 const { bytesRead, buffer } = await unixRead({ binding: mock, buffer: readBuffer, offset: 0, length: 8, fsReadAsync })72 assert.strictEqual(bytesRead, 8)73 assert.strictEqual(buffer, readBuffer)74 assert.deepStrictEqual(buffer, Buffer.alloc(8, 255))75 })76 it('handles retryable errors', async () => {77 const readBuffer = Buffer.alloc(8, 0)78 const fsReadAsync = sequenceCalls(makeFsReadError('EAGAIN'), makeFsReadError('EWOULDBLOCK'), makeFsReadError('EINTR'), makeFsRead(8, 255))79 const { bytesRead, buffer } = await unixRead({ binding: mock, buffer: readBuffer, offset: 0, length: 8, fsReadAsync })80 assert.strictEqual(bytesRead, 8)81 assert.strictEqual(buffer, readBuffer)82 assert.deepStrictEqual(buffer, Buffer.alloc(8, 255))83 })84 it('rejects read errors', async () => {85 const readBuffer = Buffer.alloc(8, 0)86 await shouldReject(unixRead({ binding: mock, buffer: readBuffer, offset: 0, length: 8, fsReadAsync: makeFsReadError('Error') }))87 })88 it('rejects a canceled error if port closes after read a retryable error', async () => {89 const readBuffer = Buffer.alloc(8, 0)90 const fsReadAsync: any = () => {91 (mock as any).isOpen = false92 makeFsReadError('EAGAIN')()93 }94 const err: BindingsError = await shouldReject(unixRead({ binding: mock, buffer: readBuffer, offset: 0, length: 8, fsReadAsync }))95 assert.isTrue(err.canceled)96 })97 it('rejects a disconnected error when fsread errors a disconnect error', async () => {98 const readBuffer = Buffer.alloc(8, 0)99 const fsReadAsync = makeFsReadError('EBADF')100 const err = await shouldReject(unixRead({ binding: mock, buffer: readBuffer, offset: 0, length: 8, fsReadAsync }))101 assert.isTrue(err.disconnect)102 })...
read-buffer.js
Source:read-buffer.js
1module.exports = { ReadBuffer }2/**3 * @param {Buffer} buffer 4 * @param {number} [offset]5 */6function ReadBuffer(buffer, offset) {7 /** @protected */8 this._buf = typeof offset === 'number'9 ? buffer.subarray(offset, buffer.length)10 : buffer;11 /** @protected */12 this._readOffset = 0;13}14Object.defineProperty(ReadBuffer.prototype, 'buffer', {15 get: function () {16 return this._buf;17 },18 enumerable: true,19 configurable: true,20})21Object.defineProperty(ReadBuffer.prototype, 'position', {22 get: function () {23 return this._readOffset;24 },25 enumerable: true,26 configurable: true,27})28Object.defineProperty(ReadBuffer.prototype, 'length', {29 get: function () {30 return this._buf.length;31 },32 enumerable: true,33 configurable: true,34})35/** @param {number} byteOffset */36ReadBuffer.prototype.seek = function seek(byteOffset) {37 this._readOffset += byteOffset;38 return this._readOffset;39}40/** @param {number} [offset] */41ReadBuffer.prototype.readUInt16BE = function readUInt16BE(offset) {42 return this._readNumberValue(this._buf.readUInt16BE, 2, offset)43}44/** @type {ReadBuffer['readUInt16BE']} */45ReadBuffer.prototype.readUint16BE = ReadBuffer.prototype.readUInt16BE;46/** @param {number} [offset] */47ReadBuffer.prototype.readUInt8 = function readUInt8(offset) {48 return this._readNumberValue(this._buf.readUInt8, 1, offset)49}50/**51 * @protected52 * @template T53 * @param {Extract<T, (offset: number) => any>} fn 54 * @param {number} byteSize55 * @param {number} [offset]56 * @return {ReturnType<T>}57 */58ReadBuffer.prototype._readNumberValue = function _readNumberValue(fn, byteSize, offset) {59 if (typeof offset === 'number') {60 return fn.call(this._buf, offset)61 }62 const value = fn.call(this._buf, this._readOffset)63 this._readOffset += byteSize;64 return value;65}66/**67 * @param {BufferEncoding} encoding 68 */69ReadBuffer.prototype.readStringNT = function readStringNT(encoding) {70 let nullPos = this._buf.length;71 for (let i = this._readOffset; i < this._buf.length; ++i) {72 if (this._buf[i] === 0x00) {73 nullPos = i;74 break;75 }76 }77 const value = this._buf.subarray(this._readOffset, this._readOffset + nullPos)78 this._readOffset = nullPos + 1;79 return value.toString(encoding)80}81/**82 * @param {number} byteSize83 * @param {number} [offset]84 */85ReadBuffer.prototype.readArrayBuffer = function readArrayBuffer(byteSize, offset) {86 if (typeof offset === 'number') {87 return this._buf.subarray(offset, offset + byteSize)88 }89 const value = this._buf.subarray(this._readOffset, this._readOffset + byteSize)90 this._readOffset += byteSize;91 return value;...
Using AI Code Generation
1var wpt = require('wpt');2var wpt = new WebPageTest('www.webpagetest.org', 'A.1234567890abcdef1234567890abcdef');3wpt.runTest(url, function(err, data) {4 if (err) {5 console.log(err);6 } else {7 var testId = data.data.testId;8 wpt.getTestResults(testId, function(err, data) {9 if (err) {10 console.log(err);11 } else {12 var data = data.data;13 var run = data.runs[1];14 var firstView = run.firstView;15 var requests = firstView.requests;16 var requestsLength = requests.length;17 var i = 0;18 for (i = 0; i < requestsLength; i++) {19 var request = requests[i];20 var url = request.url;21 var mimeType = request.mimeType;22 var responseCode = request.responseCode;23 var response = request.response;24 var responseLength = response.length;25 var j = 0;26 for (j = 0; j < responseLength; j++) {27 var buffer = response[j];28 var bufferLength = buffer.length;29 var k = 0;30 for (k = 0; k < bufferLength; k++) {31 var bufferChar = buffer[k];32 }33 }34 }35 }36 });37 }38});
Using AI Code Generation
1var wpt = require('wpt');2var fs = require('fs');3 if (err) console.log(err);4 fs.writeFile('test.html', data, function(err) {5 if (err) console.log(err);6 console.log('File saved');7 });8});9var wpt = require('wpt');10var fs = require('fs');11 if (err) console.log(err);12 fs.writeFile('test.html', data, function(err) {13 if (err) console.log(err);14 console.log('File saved');15 });16});17var wpt = require('wpt');18var fs = require('fs');19 if (err) console.log(err);20 fs.writeFile('test.html', data, function(err) {21 if (err) console.log(err);22 console.log('File saved');23 });24});25var wpt = require('wpt');26var fs = require('fs');27 if (err) console.log(err);28 fs.writeFile('test.html', data, function(err) {29 if (err) console.log(err);30 console.log('File saved');31 });32});
Using AI Code Generation
1var wptoolkit = require('wptoolkit');2console.log(wptoolkit.readBuffer('test.txt'));3var wptoolkit = require('wptoolkit');4console.log(wptoolkit.readBuffer('test.txt'));5var wptoolkit = require('wptoolkit');6console.log(wptoolkit.readBuffer('test.txt'));7var wptoolkit = require('wptoolkit');8console.log(wptoolkit.readBuffer('test.txt'));9var wptoolkit = require('wptoolkit');10console.log(wptoolkit.readBuffer('test.txt'));11var wptoolkit = require('wptoolkit');12console.log(wptoolkit.readBuffer('test.txt'));13var wptoolkit = require('wptoolkit');14console.log(wptoolkit.readBuffer('test.txt'));15var wptoolkit = require('wptoolkit');16console.log(wptoolkit.readBuffer('test.txt'));17var wptoolkit = require('wptoolkit');18console.log(wptoolkit.readBuffer('test.txt'));19var wptoolkit = require('wptoolkit');20console.log(wptoolkit.readBuffer('test.txt'));21var wptoolkit = require('wptoolkit');22console.log(wptoolkit.readBuffer('test.txt'));23var wptoolkit = require('wptoolkit
Using AI Code Generation
1var wptools = require('wptools');2var page = wptools.page('Barack Obama');3page.readBuffer(function(err, buffer) {4 console.log(buffer);5});6var wptools = require('wptools');7var page = wptools.page('Barack Obama');8var cheerio = require('cheerio');9page.readBuffer(function(err, buffer) {10 var $ = cheerio.load(buffer);11 var $table = $('table.infobox');12 console.log($table.html());13});
Using AI Code Generation
1var wpt = require('wpt');2var fs = require('fs');3var path = require('path');4var filePath = path.join(__dirname, 'test.txt');5var buffer = wpt.readBuffer(filePath);6console.log(buffer.toString());7var wpt = require('wpt');8var fs = require('fs');9var path = require('path');10var filePath = path.join(__dirname, 'test.txt');11var buffer = wpt.readBuffer(filePath);12console.log(buffer.toString());13var wpt = require('wpt');14var fs = require('fs');15var path = require('path');16var filePath = path.join(__dirname, 'test.txt');17var buffer = wpt.readBuffer(filePath);18console.log(buffer.toString());19var wpt = require('wpt');20var fs = require('fs');21var path = require('path');22var filePath = path.join(__dirname, 'test.txt');23var buffer = wpt.readBuffer(filePath);24console.log(buffer.toString());25var wpt = require('wpt');26var fs = require('fs');27var path = require('path');28var filePath = path.join(__dirname, 'test.txt');29var buffer = wpt.readBuffer(filePath);30console.log(buffer.toString());31var wpt = require('wpt');32var fs = require('fs');33var path = require('path');34var filePath = path.join(__
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!