blob: df67bea4c9819ec52d3d60e17f46ad9238dc3a6d [file] [log] [blame]
# Copyright 2019 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.
from collections import defaultdict
def _error_message(message, feature, other_feature=None):
message = 'runtime_enabled_features.json5: {}: {}'.format(feature, message)
if other_feature:
message += ': {}'.format(other_feature)
return message
def _validate_runtime_features_graph(features):
"""
Raises AssertionError when sanity check failed.
@param features: a List[Dict]. See origin_trials().
@returns None
"""
feature_pool = {str(f['name']) for f in features}
origin_trial_pool = {
str(f['name'])
for f in features if f['origin_trial_feature_name']
}
for f in features:
assert not f['implied_by'] or not f['depends_on'], _error_message(
'Only one of implied_by and depends_on is allowed', f['name'])
for d in f['depends_on']:
assert d in feature_pool, _error_message(
'Depends on non-existent-feature', f['name'], d)
for i in f['implied_by']:
assert i in feature_pool, _error_message(
'Implied by non-existent-feature', f['name'], i)
assert f['origin_trial_feature_name'] or i not in origin_trial_pool, \
_error_message(
'A feature must be in origin trial if implied by an origin trial feature',
f['name'], i)
graph = {
str(feature['name']): feature['depends_on'] + feature['implied_by']
for feature in features
}
path = set()
def has_cycle(vertex):
path.add(vertex)
for neighbor in graph[vertex]:
if neighbor in path or has_cycle(neighbor):
return True
path.remove(vertex)
return False
for f in features:
assert not has_cycle(str(f['name'])), _error_message(
'Cycle found in depends_on/implied_by graph', f['name'])
def origin_trials(features):
"""
This function returns all features that are in origin trial.
The dependency is considered in origin trial if itself is in origin trial
or any of its dependencies are in origin trial. Propagate dependency
tag use DFS can find all features that are in origin trial.
@param features: a List[Dict]. Each Dict must have keys 'name',
'depends_on', 'implied_by' and 'origin_trial_feature_name'
(see runtime_enabled_features.json5).
@returns Set[str(runtime feature name)]
"""
_validate_runtime_features_graph(features)
origin_trials_set = set()
graph = defaultdict(list)
for feature in features:
for dependency in feature['depends_on']:
graph[dependency].append(str(feature['name']))
def dfs(node):
origin_trials_set.add(node)
for dependent in graph[node]:
if dependent not in origin_trials_set:
dfs(dependent)
for feature in features:
if feature['origin_trial_feature_name']:
dfs(str(feature['name']))
return origin_trials_set