Summary: https://github.com/facebook/yoga/pull/1116 added a change to the test generator "gentests.rb" to support a newer version of chromedriver, along with a change to the enum generator (not touched in this diff) to produce code consistent with the current tests, which seem to have been manually edited since last generation. I had trouble running the test generator locally, because it relies on unversioned third-party dependencies, whose APIs change. Looking at source history, it seems like each time someone wants to run the script, they end up updating its syntax to match whatever versions they pull in. This change adds a Gemfile and lock so that that the version of "watir" is locked, and so that we will also automatically pull in a consistent "chomedriver" version via the "webdrivers" gem. It includes the updates from the PR to be consistent with already output tests, and I have also updated the copyright header generation to no longer create lint warnings on newly generated tests (some of the previous ones were fixed manually it looks like). The test generator would still produce bodies which would fail clang-format, and were manually edited (causing generation to emit new lint warnings), so I updated the generator to suppress clang-format in the body of the generated files. Three tests, around the interaction of minimum dimensions and flexible children produce different results in Chrome now compared to when the tests were added, so running `gentests.rb` creates tests which break UTs. This doesn't seem like any sort of rounding, or device specific difference, so I have disabled these tests for now. While digging around, it does look like Chrome periodically will fix bugs in its own layout implementation which cause differences, like https://bugs.chromium.org/p/chromium/issues/detail?id=927066 Reviewed By: rozele, Andrey-Mishanin Differential Revision: D39907416 fbshipit-source-id: f88714ff038b42f935901783452df25eabb6ebb1
556 lines
17 KiB
JavaScript
Executable File
556 lines
17 KiB
JavaScript
Executable File
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
window.onload = function() {
|
|
checkDefaultValues();
|
|
|
|
printTest(
|
|
new CPPEmitter(),
|
|
'cpp',
|
|
document.body.children[0],
|
|
document.body.children[1],
|
|
document.body.children[2]);
|
|
|
|
printTest(
|
|
new JavaEmitter(),
|
|
'java',
|
|
document.body.children[0],
|
|
document.body.children[1],
|
|
document.body.children[2]);
|
|
|
|
printTest(
|
|
new CSEmitter(),
|
|
'cs',
|
|
document.body.children[0],
|
|
document.body.children[1],
|
|
document.body.children[2]);
|
|
|
|
printTest(
|
|
new JavascriptEmitter(),
|
|
'js',
|
|
document.body.children[0],
|
|
document.body.children[1],
|
|
document.body.children[2]);
|
|
}
|
|
|
|
function assert(condition, message) {
|
|
if (!condition) {
|
|
throw new Error(message);
|
|
}
|
|
}
|
|
|
|
function printTest(e, ext, LTRContainer, RTLContainer, genericContainer) {
|
|
e.push([
|
|
ext === 'js' ? '/**' : '/*',
|
|
' * Copyright (c) Meta Platforms, Inc. and affiliates.',
|
|
' *',
|
|
' * This source code is licensed under the MIT license found in the',
|
|
' * LICENSE file in the root directory of this source tree.',
|
|
' */',
|
|
ext === 'cpp' ? '\n// clang-format off' : '',
|
|
'// @Generated by gentest/gentest.rb from gentest/fixtures/' + document.title + '.html',
|
|
'',
|
|
]);
|
|
e.emitPrologue();
|
|
|
|
var LTRLayoutTree = calculateTree(LTRContainer);
|
|
var RTLLayoutTree = calculateTree(RTLContainer);
|
|
var genericLayoutTree = calculateTree(genericContainer);
|
|
|
|
|
|
for (var i = 0; i < genericLayoutTree.length; i++) {
|
|
e.emitTestPrologue(genericLayoutTree[i].name, genericLayoutTree[i].experiments);
|
|
|
|
if (genericLayoutTree[i].name == 'wrap_column') {
|
|
// Modify width and left values due to both safari and chrome not abiding by the
|
|
// specification. The undefined dimension of a parent should be defined by the total size
|
|
// of their children in that dimension.
|
|
// See diagram under flex-wrap header https://www.w3.org/TR/css-flexbox-1/
|
|
assert(LTRLayoutTree[0].width == 30, 'wrap_column LTR root.width should be 30');
|
|
LTRLayoutTree[0].width = 60;
|
|
assert(RTLLayoutTree[0].width == 30, 'wrap_column RTL root.width should be 30');
|
|
RTLLayoutTree[0].width = 60;
|
|
var children = RTLLayoutTree[0].children;
|
|
assert(children[0].left == 0, 'wrap_column RTL root_child0.left should be 0');
|
|
children[0].left = 30;
|
|
assert(children[1].left == 0, 'wrap_column RTL root_child0.left should be 0');
|
|
children[1].left = 30;
|
|
assert(children[2].left == 0, 'wrap_column RTL root_child2.left should be 0');
|
|
children[2].left = 30;
|
|
assert(children[3].left == -30, 'wrap_column RTL root_child3.left should be -30');
|
|
children[3].left = 0;
|
|
}
|
|
|
|
setupTestTree(
|
|
e,
|
|
undefined,
|
|
LTRLayoutTree[i],
|
|
genericLayoutTree[i],
|
|
'root',
|
|
null);
|
|
|
|
e.YGNodeCalculateLayout('root', e.YGDirectionLTR, genericLayoutTree[i].experiments);
|
|
e.push('');
|
|
|
|
assertTestTree(e, LTRLayoutTree[i], 'root', null);
|
|
e.push('');
|
|
|
|
e.YGNodeCalculateLayout('root', e.YGDirectionRTL, genericLayoutTree[i].experiments);
|
|
e.push('');
|
|
|
|
assertTestTree(e, RTLLayoutTree[i], 'root', null);
|
|
|
|
e.emitTestEpilogue(genericLayoutTree[i].experiments);
|
|
}
|
|
e.emitEpilogue();
|
|
|
|
e.print();
|
|
}
|
|
|
|
function assertTestTree(e, node, nodeName, parentName) {
|
|
e.AssertEQ(node.left, e.YGNodeLayoutGetLeft(nodeName));
|
|
e.AssertEQ(node.top, e.YGNodeLayoutGetTop(nodeName));
|
|
e.AssertEQ(node.width, e.YGNodeLayoutGetWidth(nodeName));
|
|
e.AssertEQ(node.height, e.YGNodeLayoutGetHeight(nodeName));
|
|
|
|
for (var i = 0; i < node.children.length; i++) {
|
|
e.push('');
|
|
var childName = nodeName + '_child' + i;
|
|
assertTestTree(e, node.children[i], childName, nodeName);
|
|
}
|
|
}
|
|
|
|
function checkDefaultValues() {
|
|
// Sanity check of the Yoga default values by test-template.html
|
|
[
|
|
{style:'flex-direction', value:'column'},
|
|
{style:'justify-content', value:'flex-start'},
|
|
{style:'align-content', value:'flex-start'},
|
|
{style:'align-items', value:'stretch'},
|
|
{style:'position', value:'relative'},
|
|
{style:'flex-wrap', value:'nowrap'},
|
|
{style:'overflow', value:'visible'},
|
|
{style:'flex-grow', value:'0'},
|
|
{style:'flex-shrink', value:'0'},
|
|
{style:'left', value:'undefined'},
|
|
{style:'top', value:'undefined'},
|
|
{style:'right', value:'undefined'},
|
|
{style:'bottom', value:'undefined'},
|
|
{style:'display', value:'flex'},
|
|
].forEach(function(item) {
|
|
assert(isDefaultStyleValue(item.style, item.value),
|
|
item.style + ' should be ' + item.value);
|
|
});
|
|
}
|
|
|
|
function setupTestTree(e, parent, node, genericNode, nodeName, parentName, index) {
|
|
e.emitTestTreePrologue(nodeName);
|
|
|
|
for (var style in node.style) {
|
|
// Skip position info for root as it messes up tests
|
|
if (node.declaredStyle[style] === "" &&
|
|
(style == 'position' ||
|
|
style == 'left' ||
|
|
style == 'top' ||
|
|
style == 'right' ||
|
|
style == 'bottom' ||
|
|
style == 'width' ||
|
|
style == 'height')) {
|
|
continue;
|
|
}
|
|
|
|
if (!isDefaultStyleValue(style, node.style[style])) {
|
|
switch (style) {
|
|
case 'direction':
|
|
e.YGNodeStyleSetDirection(nodeName, directionValue(e, node.style[style]));
|
|
break;
|
|
case 'flex-direction':
|
|
e.YGNodeStyleSetFlexDirection(nodeName, flexDirectionValue(e, node.style[style]));
|
|
break;
|
|
case 'justify-content':
|
|
e.YGNodeStyleSetJustifyContent(nodeName, justifyValue(e, node.style[style]));
|
|
break;
|
|
case 'align-content':
|
|
e.YGNodeStyleSetAlignContent(nodeName, alignValue(e, node.style[style]));
|
|
break;
|
|
case 'align-items':
|
|
e.YGNodeStyleSetAlignItems(nodeName, alignValue(e, node.style[style]));
|
|
break;
|
|
case 'align-self':
|
|
if (!parent || node.style[style] !== parent.style['align-items']) {
|
|
e.YGNodeStyleSetAlignSelf(nodeName, alignValue(e, node.style[style]));
|
|
}
|
|
break;
|
|
case 'position':
|
|
e.YGNodeStyleSetPositionType(nodeName, positionValue(e, node.style[style]));
|
|
break;
|
|
case 'flex-wrap':
|
|
e.YGNodeStyleSetFlexWrap(nodeName, wrapValue(e, node.style[style]));
|
|
break;
|
|
case 'overflow':
|
|
e.YGNodeStyleSetOverflow(nodeName, overflowValue(e, node.style[style]));
|
|
break;
|
|
case 'flex-grow':
|
|
e.YGNodeStyleSetFlexGrow(nodeName, node.style[style]);
|
|
break;
|
|
case 'flex-shrink':
|
|
e.YGNodeStyleSetFlexShrink(nodeName, node.style[style]);
|
|
break;
|
|
case 'flex-basis':
|
|
e.YGNodeStyleSetFlexBasis(nodeName, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'left':
|
|
if (genericNode.rawStyle.indexOf('start:') >= 0) {
|
|
e.YGNodeStyleSetPosition(nodeName, e.YGEdgeStart, pointValue(e, node.style[style]));
|
|
} else {
|
|
e.YGNodeStyleSetPosition(nodeName, e.YGEdgeLeft, pointValue(e, node.style[style]));
|
|
}
|
|
break;
|
|
case 'top':
|
|
e.YGNodeStyleSetPosition(nodeName, e.YGEdgeTop, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'right':
|
|
if (genericNode.rawStyle.indexOf('end:') >= 0) {
|
|
e.YGNodeStyleSetPosition(nodeName, e.YGEdgeEnd, pointValue(e, node.style[style]));
|
|
} else {
|
|
e.YGNodeStyleSetPosition(nodeName, e.YGEdgeRight, pointValue(e, node.style[style]));
|
|
}
|
|
break;
|
|
case 'bottom':
|
|
e.YGNodeStyleSetPosition(nodeName, e.YGEdgeBottom, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'margin-left':
|
|
if (genericNode.rawStyle.indexOf('margin-start:') >= 0) {
|
|
e.YGNodeStyleSetMargin(nodeName, e.YGEdgeStart, pointValue(e, node.style[style]));
|
|
} else {
|
|
e.YGNodeStyleSetMargin(nodeName, e.YGEdgeLeft, pointValue(e, node.style[style]));
|
|
}
|
|
break;
|
|
case 'margin-top':
|
|
e.YGNodeStyleSetMargin(nodeName, e.YGEdgeTop, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'margin-right':
|
|
if (genericNode.rawStyle.indexOf('margin-end:') >= 0) {
|
|
e.YGNodeStyleSetMargin(nodeName, e.YGEdgeEnd, pointValue(e, node.style[style]));
|
|
} else {
|
|
e.YGNodeStyleSetMargin(nodeName, e.YGEdgeRight, pointValue(e, node.style[style]));
|
|
}
|
|
break;
|
|
case 'margin-bottom':
|
|
e.YGNodeStyleSetMargin(nodeName, e.YGEdgeBottom, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'padding-left':
|
|
if (genericNode.rawStyle.indexOf('padding-start:') >= 0) {
|
|
e.YGNodeStyleSetPadding(nodeName, e.YGEdgeStart, pointValue(e, node.style[style]));
|
|
} else {
|
|
e.YGNodeStyleSetPadding(nodeName, e.YGEdgeLeft, pointValue(e, node.style[style]));
|
|
}
|
|
break;
|
|
case 'padding-top':
|
|
e.YGNodeStyleSetPadding(nodeName, e.YGEdgeTop, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'padding-right':
|
|
if (genericNode.rawStyle.indexOf('padding-end:') >= 0) {
|
|
e.YGNodeStyleSetPadding(nodeName, e.YGEdgeEnd, pointValue(e, node.style[style]));
|
|
} else {
|
|
e.YGNodeStyleSetPadding(nodeName, e.YGEdgeRight, pointValue(e, node.style[style]));
|
|
}
|
|
break;
|
|
case 'padding-bottom':
|
|
e.YGNodeStyleSetPadding(nodeName, e.YGEdgeBottom, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'border-left-width':
|
|
if (genericNode.rawStyle.indexOf('border-start-width:') >= 0) {
|
|
e.YGNodeStyleSetBorder(nodeName, e.YGEdgeStart, pointValue(e, node.style[style]));
|
|
} else {
|
|
e.YGNodeStyleSetBorder(nodeName, e.YGEdgeLeft, pointValue(e, node.style[style]));
|
|
}
|
|
break;
|
|
case 'border-top-width':
|
|
e.YGNodeStyleSetBorder(nodeName, e.YGEdgeTop, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'border-right-width':
|
|
if (genericNode.rawStyle.indexOf('border-end-width:') >= 0) {
|
|
e.YGNodeStyleSetBorder(nodeName, e.YGEdgeEnd, pointValue(e, node.style[style]));
|
|
} else {
|
|
e.YGNodeStyleSetBorder(nodeName, e.YGEdgeRight, pointValue(e, node.style[style]));
|
|
}
|
|
break;
|
|
case 'border-bottom-width':
|
|
e.YGNodeStyleSetBorder(nodeName, e.YGEdgeBottom, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'width':
|
|
e.YGNodeStyleSetWidth(nodeName, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'min-width':
|
|
e.YGNodeStyleSetMinWidth(nodeName, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'max-width':
|
|
e.YGNodeStyleSetMaxWidth(nodeName, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'height':
|
|
e.YGNodeStyleSetHeight(nodeName, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'min-height':
|
|
e.YGNodeStyleSetMinHeight(nodeName, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'max-height':
|
|
e.YGNodeStyleSetMaxHeight(nodeName, pointValue(e, node.style[style]));
|
|
break;
|
|
case 'display':
|
|
e.YGNodeStyleSetDisplay(nodeName, displayValue(e, node.style[style]))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (parentName) {
|
|
e.YGNodeInsertChild(parentName, nodeName, index);
|
|
}
|
|
|
|
for (var i = 0; i < node.children.length; i++) {
|
|
e.push('');
|
|
var childName = nodeName + '_child' + i;
|
|
setupTestTree(
|
|
e,
|
|
node,
|
|
node.children[i],
|
|
genericNode.children[i],
|
|
childName,
|
|
nodeName,
|
|
i);
|
|
}
|
|
}
|
|
|
|
function overflowValue(e, value) {
|
|
switch (value) {
|
|
case 'visible': return e.YGOverflowVisible;
|
|
case 'hidden': return e.YGOverflowHidden;
|
|
}
|
|
}
|
|
|
|
function wrapValue(e, value) {
|
|
switch (value) {
|
|
case 'wrap': return e.YGWrapWrap;
|
|
case 'wrap-reverse': return e.YGWrapWrapReverse;
|
|
case 'nowrap': return e.YGWrapNoWrap;
|
|
}
|
|
}
|
|
|
|
function flexDirectionValue(e, value) {
|
|
switch (value) {
|
|
case 'row': return e.YGFlexDirectionRow;
|
|
case 'row-reverse': return e.YGFlexDirectionRowReverse;
|
|
case 'column': return e.YGFlexDirectionColumn;
|
|
case 'column-reverse': return e.YGFlexDirectionColumnReverse;
|
|
}
|
|
}
|
|
|
|
function justifyValue(e, value) {
|
|
switch (value) {
|
|
case 'center': return e.YGJustifyCenter;
|
|
case 'space-around': return e.YGJustifySpaceAround;
|
|
case 'space-between': return e.YGJustifySpaceBetween;
|
|
case 'space-evenly': return e.YGJustifySpaceEvenly;
|
|
case 'flex-start': return e.YGJustifyFlexStart;
|
|
case 'flex-end': return e.YGJustifyFlexEnd;
|
|
}
|
|
}
|
|
|
|
function positionValue(e, value) {
|
|
switch (value) {
|
|
case 'absolute': return e.YGPositionTypeAbsolute;
|
|
default: return e.YGPositionTypeRelative
|
|
}
|
|
}
|
|
|
|
function directionValue(e, value) {
|
|
switch (value) {
|
|
case 'ltr': return e.YGDirectionLTR;
|
|
case 'rtl': return e.YGDirectionRTL;
|
|
case 'inherit': return e.YGDirectionInherit;
|
|
}
|
|
}
|
|
|
|
function alignValue(e, value) {
|
|
switch (value) {
|
|
case 'auto': return e.YGAlignAuto;
|
|
case 'center': return e.YGAlignCenter;
|
|
case 'stretch': return e.YGAlignStretch;
|
|
case 'flex-start': return e.YGAlignFlexStart;
|
|
case 'flex-end': return e.YGAlignFlexEnd;
|
|
case 'space-between': return e.YGAlignSpaceBetween;
|
|
case 'space-around': return e.YGAlignSpaceAround;
|
|
case 'baseline': return e.YGAlignBaseline;
|
|
}
|
|
}
|
|
|
|
function pointValue(e, value) {
|
|
switch (value) {
|
|
case 'auto': return e.YGAuto;
|
|
case 'undefined': return e.YGUndefined;
|
|
default: return value;
|
|
}
|
|
}
|
|
|
|
function displayValue(e, value){
|
|
switch(value){
|
|
case 'flex': return e.YGDisplayFlex;
|
|
case 'none': return e.YGDisplayNone;
|
|
}
|
|
}
|
|
|
|
var DEFAULT_STYLES = new Map();
|
|
|
|
function isDefaultStyleValue(style, value) {
|
|
let defaultStyle = DEFAULT_STYLES.get(style);
|
|
if (defaultStyle == null) {
|
|
switch (style) {
|
|
case 'position':
|
|
defaultStyle = new Set(['relative']);;
|
|
break;
|
|
|
|
case 'left':
|
|
case 'top':
|
|
case 'right':
|
|
case 'bottom':
|
|
case 'start':
|
|
case 'end':
|
|
defaultStyle = new Set(['undefined']);
|
|
break;
|
|
|
|
case 'min-height':
|
|
case 'min-width':
|
|
defaultStyle = new Set(['0', '0px', 'auto']);
|
|
break;
|
|
|
|
default:
|
|
var node = document.getElementById('default');
|
|
defaultStyle = new Set([getComputedStyle(node, null)[style]]);
|
|
break;
|
|
}
|
|
DEFAULT_STYLES.set(style, defaultStyle);
|
|
}
|
|
return DEFAULT_STYLES.get(style).has(value);
|
|
}
|
|
|
|
function getRoundedSize(node) {
|
|
var boundingRect = node.getBoundingClientRect();
|
|
return {
|
|
width: Math.round(boundingRect.right) - Math.round(boundingRect.left),
|
|
height: Math.round(boundingRect.bottom) - Math.round(boundingRect.top)
|
|
};
|
|
}
|
|
|
|
function calculateTree(root, roundToPixelGrid) {
|
|
var rootLayout = [];
|
|
|
|
for (var i = 0; i < root.children.length; i++) {
|
|
var child = root.children[i];
|
|
var layout = {
|
|
name: child.id !== '' ? child.id : 'INSERT_NAME_HERE',
|
|
left: child.offsetLeft + child.parentNode.clientLeft,
|
|
top: child.offsetTop + child.parentNode.clientTop,
|
|
width: child.offsetWidth,
|
|
height: child.offsetHeight,
|
|
children: calculateTree(child, roundToPixelGrid),
|
|
style: getYogaStyle(child),
|
|
declaredStyle: child.style,
|
|
rawStyle: child.getAttribute('style'),
|
|
experiments: child.getAttribute('experiments')
|
|
? child.getAttribute('experiments').split(' ')
|
|
: [],
|
|
};
|
|
|
|
var size = getRoundedSize(child);
|
|
layout.width = size.width;
|
|
layout.height = size.height;
|
|
|
|
rootLayout.push(layout);
|
|
}
|
|
|
|
return rootLayout;
|
|
}
|
|
|
|
function getYogaStyle(node) {
|
|
return [
|
|
'direction',
|
|
'flex-direction',
|
|
'justify-content',
|
|
'align-content',
|
|
'align-items',
|
|
'align-self',
|
|
'position',
|
|
'flex-wrap',
|
|
'overflow',
|
|
'flex-grow',
|
|
'flex-shrink',
|
|
'flex-basis',
|
|
'left',
|
|
'top',
|
|
'right',
|
|
'bottom',
|
|
'margin-left',
|
|
'margin-top',
|
|
'margin-right',
|
|
'margin-bottom',
|
|
'padding-left',
|
|
'padding-top',
|
|
'padding-right',
|
|
'padding-bottom',
|
|
'border-left-width',
|
|
'border-top-width',
|
|
'border-right-width',
|
|
'border-bottom-width',
|
|
'width',
|
|
'min-width',
|
|
'max-width',
|
|
'height',
|
|
'min-height',
|
|
'max-height',
|
|
'display',
|
|
].reduce(function(map, key) {
|
|
map[key] = node.style[key] || getComputedStyle(node, null).getPropertyValue(key);
|
|
return map;
|
|
}, {});
|
|
}
|
|
|
|
var Emitter = function(lang, indent) {
|
|
this.lang = lang;
|
|
this.indent = indent;
|
|
this.indents = [];
|
|
this.lines = [];
|
|
};
|
|
|
|
Emitter.prototype = Object.create(Object.prototype, {
|
|
constructor:{value:Emitter},
|
|
|
|
pushIndent:{value:function() {
|
|
this.indents.push(this.indent);
|
|
}},
|
|
|
|
popIndent:{value:function() {
|
|
this.indents.pop();
|
|
}},
|
|
|
|
push:{value:function(line) {
|
|
if (line instanceof Array) {
|
|
line.forEach(function(element) {
|
|
this.push(element);
|
|
}, this);
|
|
return;
|
|
} else if (line.length > 0) {
|
|
line = this.indents.join('') + line;
|
|
}
|
|
this.lines.push(line);
|
|
}},
|
|
|
|
print:{value:function() {
|
|
console.log(this.lines.join('\n'));
|
|
}},
|
|
});
|