Skip to content

Instantly share code, notes, and snippets.

@queengooborg
Last active March 30, 2019 14:20
Show Gist options
  • Save queengooborg/db303f7e72a582d2bf89d84bc4d035e2 to your computer and use it in GitHub Desktop.
Save queengooborg/db303f7e72a582d2bf89d84bc4d035e2 to your computer and use it in GitHub Desktop.
Check against IDL files in browsers and a list for implementation
import os
import sys
import widlparser
prefix_list = ["", "webkit", "Webkit", "WebKit", "Moz", "moz"]
browser = 'firefox'
if len(sys.argv) > 1:
browser = sys.argv[1]
if browser == 'firefox':
webidl_path = "/Users/vinyldarkscratch/Developer/git/gecko-dev/dom/webidl"
elif browser == 'chrome':
webidl_path = "/Users/vinyldarkscratch/Developer/git/chromium/src/third_party/blink"
elif browser == 'safari':
webidl_path = "/Users/vinyldarkscratch/Developer/WebKit/Source"
data = ""
with open("/Users/vinyldarkscratch/Desktop/%s_null.txt" %browser) as f:
entries_to_check = [l.replace("\n", "") for l in f.readlines() if l.startswith("api.")]
def dir_walk(path):
response = []
for root, subdirs, files in os.walk(path):
response += [os.path.join(root, f) for f in files if (f.endswith('.idl') or f.endswith('.webidl'))]
for s in subdirs:
response += dir_walk(s)
return response
def prefixes(name):
return [p + name for p in prefix_list]
def get_interface(name, widl):
definitions = []
implements = []
for w in widl:
if w.name in prefixes(name):
if w.idlType == 'interface':
definitions.append(w)
elif w.idlType == 'implements':
implements.append(w.implements)
for i in implements:
definitions += get_interface(i, widl)
return definitions
def which_prefix(entry):
for i in range(1, len(prefix_list)):
if entry.name.startswith(prefix_list[i]):
return " - Prefix: {0}".format(prefix_list[i])
return ""
def get_runtime_flags(entry):
for flag in entry.extendedAttributes:
if flag.name in ['RuntimeEnabled', 'Pref']:
return " - Flag: {0}".format(flag.attribute.value or flag.attribute.string)
return ""
def check_entry(entry):
e = entry.split(".")
for w in widl:
if w.name in prefixes(e[1]):
if len(e) == 2:
return "found{0}{1}".format(get_runtime_flags(w), which_prefix(w))
elif len(e) >= 4:
return "feature_requires_manual_review"
elif len(e) == 3:
if hasattr(w, 'members'):
for d in get_interface(w.name, widl):
for m in d.members:
if m.name in prefixes(e[2]):
return "found{0}{1}".format(get_runtime_flags(m), which_prefix(m))
return "not_defined"
return "no_idl"
files = sorted(dir_walk(webidl_path))
for fn in files:
with open(os.path.join(webidl_path, fn), 'r') as f:
new_data = f.read()
data += new_data
with open("{0}.idl".format(browser), 'w') as f:
f.write(data)
widl = widlparser.parser.Parser(data)
with open("{0}.data.txt".format(browser), 'w') as f:
for entry in entries_to_check:
c = check_entry(entry)
f.write("%s - %s\n" %(entry, c))
'use strict';
const path = require('path');
const compareVersions = require('compare-versions');
const browser_to_test = 'firefox';
const version_added_to_test = null;
/**
* Version is Bool check.
*
* This checker aims at improving data quality
* by detecting inconsistent information.
*/
class VersionBoolChecker
{
/**
* @param {object} data
* @returns {Array<object>}
*/
check(data) {
return this.checkSubfeatures(data);
}
/**
* @param {object} data
* @param {array} path
* @returns {Array<object>}
*/
checkSubfeatures(data, path = []) {
let allErrors = [];
// Check this feature.
if (this.isFeature(data)) {
const feature = path.length ? path[path.length - 1] : 'ROOT';
const featurePath = path.length ? path.slice(0, path.length - 1).join('.') : '';
const errors = this.checkFeature(data);
if (errors.length) {
allErrors.push({
feature,
path,
errors
});
}
}
// Check sub-features.
const keys = Object.keys(data).filter(key => key != '__compat');
keys.forEach(key => {
allErrors = [
...allErrors,
...this.checkSubfeatures(data[key], [...path, key])
];
});
return allErrors;
}
/**
* @param {object} data
* @returns {Array<object>}
*/
checkFeature(data) {
let errors = [];
// Add errors
Object.keys(data['__compat']['support']).forEach(browser => {
if (browser == browser_to_test && data['__compat']['support'][browser]['version_added'] === version_added_to_test) {
const errortype = 'version_added';
errors.push({errortype, browser});
}
});
return errors;
}
/**
* @param {object} data
* @returns {boolean}
*/
isFeature(data) {
return '__compat' in data;
}
/**
* @param {object} compatData
* @returns {Array<string>}
*/
extractUnsupportedBrowsers(compatData) {
return this.extractBrowsers(compatData, data => data.version_added === false || typeof data.version_removed !== 'undefined' && data.version_removed !== false);
}
/**
* @param {object} compatData
* @returns {Array<string>}
*/
extractSupportedBrowsersWithVersion(compatData) {
return this.extractBrowsers(compatData, data => typeof(data.version_added) === 'string');
}
/*
* @param {object} compatData
* @returns {string}
*/
getVersionAdded(compatData) {
var version_added = null;
if (typeof(compatData.version_added) === 'string')
return compatData.version_added;
if (compatData.constructor === Array) {
for (var i = compatData.length - 1; i >= 0; i--) {
var va = compatData[i].version_added;
if (typeof(va) === 'string' && (version_added == null || compareVersions(version_added, va) == 1))
version_added = va;
}
}
return version_added;
}
/*
* @param {string} a
* @param {string} b
* @returns {boolean}
*/
isVersionAddedGreater(a, b) {
var a_version_added = this.getVersionAdded(a);
var b_version_added = this.getVersionAdded(b);
if (typeof(a_version_added) === 'string' && typeof(b_version_added) === 'string')
return compareVersions(a_version_added, b_version_added) == -1;
return false;
}
/**
*
* @param {object} compatData
* @param {callback} callback
* @returns {boolean}
*/
extractBrowsers(compatData, callback)
{
return Object.keys(compatData.support).filter(browser => {
const browserData = compatData.support[browser];
if (Array.isArray(browserData)) {
return browserData.every(callback);
} else if (typeof browserData === 'object') {
return callback(browserData);
} else {
return false;
}
});
}
}
function testVersionBool(filename) {
let data = require(filename);
const checker = new VersionBoolChecker();
const errors = checker.check(data);
if (errors.length) {
const relativeFilename = path.relative(process.cwd(), filename);
console.error(`\x1b[34m Found \x1b[1m${errors.length}\x1b[0m\x1b[34m inconsistent feature(s) in \x1b[3m${relativeFilename}:\x1b[0m`);
errors.forEach(({ feature, path, errors }) => {
console.error(`\x1b[34m → \x1b[1m${errors.length}\x1b[0m\x1b[34m × \x1b[1m${feature}\x1b[0m\x1b[34m [\x1b[3m${path.join('.')}\x1b[0m\x1b[34m]: `);
errors.forEach(({ errortype, browser }) => {
console.error(`\x1b[34m → version_added for \x1b[1m${browser}\x1b[0m\x1b[34m was declared as \x1b[1m${version_added_to_test}\x1b[0m`);
});
})
return true;
} else {
console.log('\x1b[32m Version is Bool – OK \x1b[0m');
return false;
}
}
module.exports = testVersionBool;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment