Commit cc2105f8 authored by Charlie Hu's avatar Charlie Hu Committed by Chromium LUCI CQ

[Document Policy] Add PRESUBMIT test on code generation

This CL adds a PRESUBMIT check on document policy code generation
to compare the generated .cc file with the reference file.

Note: the test is suffixed with '_presubmit_only', so that it will
be excluded from 'run_blinkpy_tests.py' task, where jinja2 module
is not available.

Bug: 1166698
Change-Id: I4d2a1f3a9960c79e79d526ab8445d0ade74b4143
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2633734
Commit-Queue: Charlie Hu <chenleihu@google.com>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845796}
parent e8fe9b25
...@@ -41,14 +41,15 @@ def _RunTests(input_api, output_api): ...@@ -41,14 +41,15 @@ def _RunTests(input_api, output_api):
tests = [{ tests = [{
'file_name': 'json5_generator_unittest.py', 'file_name': 'json5_generator_unittest.py',
'affected_list': [r'.*json5_generator.*', r'.*\btests[\\\/].*'] 'affected_list': [r'.*json5_generator.*', r'.*\btests[\\\/].*']
}, }, {
{
'file_name': 'make_runtime_features_utilities_unittest.py', 'file_name': 'make_runtime_features_utilities_unittest.py',
'affected_list': [r'.*make_runtime_features_utilities.*'] 'affected_list': [r'.*make_runtime_features_utilities.*']
}, }, {
{
'file_name': 'make_document_policy_features_unittest.py', 'file_name': 'make_document_policy_features_unittest.py',
'affected_list': [r'.*make_document_policy_features.*'] 'affected_list': [r'.*make_document_policy_features.*']
}, {
'file_name': 'make_document_policy_features_tests.py',
'affected_list': [r'.*make_document_policy_features.*']
}] }]
test_commands = [] test_commands = []
for test in tests: for test in tests:
......
# Copyright 2021 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Note(crbug.com/1167597): Do NOT change the file name to end with
# '_test.py' or '_unittest.py' as these files will be recognized by
# 'run_blinkpy_tests.py' task, where jinja2 module is not available.
import unittest
import os
from make_document_policy_features import DocumentPolicyFeatureWriter
from writer_test_util import path_to_test_file, WriterTest
class MakeDocumentPolicyFeaturesTest(WriterTest):
def test_default_value_control(self):
self._test_writer(
DocumentPolicyFeatureWriter, [
path_to_test_file('document_policy_default_value_control',
'input', 'document_policy_features.json5')
],
path_to_test_file('document_policy_default_value_control',
'output'))
if __name__ == "__main__":
unittest.main()
{% from 'templates/macros.tmpl' import license, source_files_for_generated_file %} {% from 'templates/macros.tmpl' import license, source_files_for_generated_file %}
{{license()}} {{license()}}
{{ source_files_for_generated_file(template_file, input_files) }}
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/feature_list.h" #include "base/feature_list.h"
...@@ -33,13 +32,8 @@ const DocumentPolicyFeatureInfoMap& GetDocumentPolicyFeatureInfoMap() { ...@@ -33,13 +32,8 @@ const DocumentPolicyFeatureInfoMap& GetDocumentPolicyFeatureInfoMap() {
{%- for flag, default_value in feature.default_value_behind_flag %} {%- for flag, default_value in feature.default_value_behind_flag %}
{%- if not loop.first %} else {% endif %} {%- if not loop.first %} else {% endif %}
if (base::FeatureList::IsEnabled(features::k{{flag}})) { if (base::FeatureList::IsEnabled(features::k{{flag}})) {
map.insert_or_assign( map.find(mojom::DocumentPolicyFeature::k{{feature.name}})->second.default_value =
mojom::DocumentPolicyFeature::k{{feature.name}}, {{parse_default_value(default_value, feature.value_type)}};
DocumentPolicyFeatureInfo {
"{{feature.document_policy_name}}",
{{parse_default_value(default_value, feature.value_type)}}
}
);
} }
{%- endfor %} {%- endfor %}
{%- endif %} {%- endif %}
......
{
// All document policy (https://w3c.github.io/webappsec-feature-policy/document-policy.html)
// features are defined here.
// All Features have to be defined in DocumentPolicyFeature enum as well
// (defined in third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom).
// The enum value has to have the same name as the feature name here.
parameters: {
// document_policy_name: "FEATURE_NAME" is used to specify the policy name
// which gets parsed from the header or the policy attribute.
document_policy_name: {},
// value type allowed in mojom::PolicyValueType which is defined in
// third_party/blink/public/mojom/feature_policy/policy_value.mojom.
value_type: {},
// valid c++ expression strings, e.g. true/false, 1.0, -1.
// or use reserved keyword 'min'/'max'.
default_value: {},
// "depends_on" specifies relationship to runtime features defined
// in "runtime_enabled_features.json5":
// depends_on: ["feature1", "feature2", ...]
// * If the depends_on features are *only* runtime features, the feature is
// available if any of the runtime features are enabled.
// * If the depends_on list includes origin trial features, the feature is
// available if any of the origin trial features are enabled.
depends_on: {
default: [],
valid_type: "list",
},
// "default_value_behind_flag" specifies default_value override values
// based on different runtime flags set.
//
// When multiple flags are set, default_value correspond to the first
// flag in the list will be used, e.g.
// default_value_behind_flag: [
// ["A", 1.0],
// ["B", 2.0],
// ]
// 1.0 will be used as default value when both flag A and B are set.
//
// Note: the runtime flags here refer to features defined in
// "third_party/blink/public/common/features.h", instead of those defined in
// "runtime_enabled_features.json5" because the latter is only available
// on renderer side, while default_value is needed from browser side as
// well.
default_value_behind_flag: {
default: [],
valid_type: "list",
}
},
data: [
{
name: "DefaultValueFeatureForTest",
// Setting document_policy_name to "", so that it will not be recognized
// by the parser, as structured header token cannot be empty.
document_policy_name: "test-feature",
value_type: "Bool",
default_value: "true",
depends_on: [],
default_value_behind_flag: [
["DocumentPolicyRuntimeFlag1ForTest", "false"],
["DocumentPolicyRuntimeFlag2ForTest", "true"],
]
},
{
name: "Default",
document_policy_name: "*",
value_type: "Bool",
default_value: "true",
depends_on: []
},
],
}
// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/no_destructor.h"
#include "base/feature_list.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/feature_policy/document_policy_features.h"
#include "third_party/blink/public/common/feature_policy/policy_value.h"
#include "third_party/blink/public/mojom/feature_policy/policy_value.mojom.h"
#include "third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom.h"
namespace blink {
const DocumentPolicyFeatureInfoMap& GetDocumentPolicyFeatureInfoMap() {
static const base::NoDestructor<DocumentPolicyFeatureInfoMap> feature_info_map([] {
DocumentPolicyFeatureInfoMap map({ {
mojom::DocumentPolicyFeature::kDefaultValueFeatureForTest,
{
"test-feature",
PolicyValue::CreateBool(true)
}
}, {
mojom::DocumentPolicyFeature::kDefault,
{
"*",
PolicyValue::CreateBool(true)
}
}, }); if (base::FeatureList::IsEnabled(features::kDocumentPolicyRuntimeFlag1ForTest)) {
map.find(mojom::DocumentPolicyFeature::kDefaultValueFeatureForTest)->second.default_value =
PolicyValue::CreateBool(false);
} else if (base::FeatureList::IsEnabled(features::kDocumentPolicyRuntimeFlag2ForTest)) {
map.find(mojom::DocumentPolicyFeature::kDefaultValueFeatureForTest)->second.default_value =
PolicyValue::CreateBool(true);
}
return map;
}());
return *feature_info_map;
}
const DocumentPolicyNameFeatureMap& GetDocumentPolicyNameFeatureMap() {
static const base::NoDestructor<DocumentPolicyNameFeatureMap> name_feature_map([] {
DocumentPolicyNameFeatureMap map;
for (const auto& entry : GetDocumentPolicyFeatureInfoMap())
map.emplace(entry.second.feature_name, entry.first);
return map;
}());
return *name_feature_map;
}
} // namespace blink
# Copyright 2021 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import contextlib
import difflib
import filecmp
import os
import shutil
import tempfile
import unittest
from json5_generator import Json5File, Writer
@contextlib.contextmanager
def tmp_dir():
tmp = tempfile.mkdtemp()
try:
yield tmp
finally:
shutil.rmtree(tmp)
def path_to_test_file(*path):
return os.path.join(os.path.dirname(__file__), 'tests', *path)
def diff(filename1, filename2):
with open(filename1) as file1:
file1_lines = file1.readlines()
with open(filename2) as file2:
file2_lines = file2.readlines()
# Use Python's difflib module so that diffing works across platforms
return ''.join(difflib.context_diff(file1_lines, file2_lines))
def is_identical_file(reference_filename, output_filename):
reference_basename = os.path.basename(reference_filename)
if not os.path.isfile(reference_filename):
print 'Missing reference file!'
print '(if adding new test, update reference files)'
print reference_basename
print
return False
if not filecmp.cmp(reference_filename, output_filename):
# cmp is much faster than diff, and usual case is "no difference",
# so only run diff if cmp detects a difference
print 'FAIL: %s' % reference_basename
print diff(reference_filename, output_filename)
return False
return True
def compare_output_dir(reference_dir, output_dir):
"""
Compares output files in both reference_dir and output_dir.
Note: this function ignores subdirectory content in both reference
dir and output_dir.
Note: reference_dir should have all ref files ending with .ref suffix.
'.ref' suffix is added to bypass code formatter on reference files.
:returns {bool}: Whether files in output dir matches files in ref dir
"""
ref_content = {
f[:-4]
for f in os.listdir(reference_dir) if f.endswith('.ref')
}
output_content = set(os.listdir(output_dir))
if ref_content != output_content:
print 'Output files does not match.'
print 'Following files are extra: {}'.format(output_content -
ref_content)
print 'Following files are missing: {}'.format(ref_content -
output_content)
return False
for file_name in ref_content:
ref_file = os.path.join(reference_dir, file_name) + '.ref'
output_file = os.path.join(output_dir, file_name)
if os.path.isdir(ref_file) and os.path.isdir(output_file):
continue
elif os.path.isdir(ref_file) or os.path.isdir(output_file):
return False
elif not is_identical_file(ref_file, output_file):
return False
return True
class WriterTest(unittest.TestCase):
def _test_writer(self, writer_class, json5_files, reference_dir):
"""
:param writer_class {Writer}: a subclass to Writer
:param json5_files {List[str]}: json5 test input files
:param reference_dir {str}: directory to expected output files
"""
with tmp_dir() as tmp:
writer = writer_class(json5_files, tmp)
writer.write_files(tmp)
writer.cleanup_files(tmp)
self.assertTrue(compare_output_dir(reference_dir, tmp))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment