1#!/usr/bin/env python 2# ***** BEGIN LICENSE BLOCK ***** 3# This Source Code Form is subject to the terms of the Mozilla Public 4# License, v. 2.0. If a copy of the MPL was not distributed with this file, 5# You can obtain one at http://mozilla.org/MPL/2.0/. 6# ***** END LICENSE BLOCK ***** 7"""configtest.py 8 9Verify the .json and .py files in the configs/ directory are well-formed. 10Further tests to verify validity would be desirable. 11 12This is also a good example script to look at to understand mozharness. 13""" 14 15from __future__ import absolute_import 16import os 17import pprint 18import sys 19 20try: 21 import simplejson as json 22except ImportError: 23 import json 24 25sys.path.insert(1, os.path.dirname(sys.path[0])) 26 27from mozharness.base.script import BaseScript 28 29 30# ConfigTest {{{1 31class ConfigTest(BaseScript): 32 config_options = [ 33 [ 34 [ 35 "--test-file", 36 ], 37 { 38 "action": "extend", 39 "dest": "test_files", 40 "help": "Specify which config files to test", 41 }, 42 ] 43 ] 44 45 def __init__(self, require_config_file=False): 46 self.config_files = [] 47 BaseScript.__init__( 48 self, 49 config_options=self.config_options, 50 all_actions=[ 51 "list-config-files", 52 "test-json-configs", 53 "test-python-configs", 54 "summary", 55 ], 56 default_actions=[ 57 "test-json-configs", 58 "test-python-configs", 59 "summary", 60 ], 61 require_config_file=require_config_file, 62 ) 63 64 def query_config_files(self): 65 """This query method, much like others, caches its runtime 66 settings in self.VAR so we don't have to figure out config_files 67 multiple times. 68 """ 69 if self.config_files: 70 return self.config_files 71 c = self.config 72 if "test_files" in c: 73 self.config_files = c["test_files"] 74 return self.config_files 75 self.debug( 76 "No --test-file(s) specified; defaulting to crawling the configs/ directory." 77 ) 78 config_files = [] 79 for root, dirs, files in os.walk(os.path.join(sys.path[0], "..", "configs")): 80 for name in files: 81 # Hardcode =P 82 if name.endswith(".json") or name.endswith(".py"): 83 if not name.startswith("test_malformed"): 84 config_files.append(os.path.join(root, name)) 85 self.config_files = config_files 86 return self.config_files 87 88 def list_config_files(self): 89 """Non-default action that is mainly here to demonstrate how 90 non-default actions work in a mozharness script. 91 """ 92 config_files = self.query_config_files() 93 for config_file in config_files: 94 self.info(config_file) 95 96 def test_json_configs(self): 97 """Currently only "is this well-formed json?" """ 98 config_files = self.query_config_files() 99 filecount = [0, 0] 100 for config_file in config_files: 101 if config_file.endswith(".json"): 102 filecount[0] += 1 103 self.info("Testing %s." % config_file) 104 contents = self.read_from_file(config_file, verbose=False) 105 try: 106 json.loads(contents) 107 except ValueError: 108 self.add_summary("%s is invalid json." % config_file, level="error") 109 self.error(pprint.pformat(sys.exc_info()[1])) 110 else: 111 self.info("Good.") 112 filecount[1] += 1 113 if filecount[0]: 114 self.add_summary( 115 "%d of %d json config files were good." % (filecount[1], filecount[0]) 116 ) 117 else: 118 self.add_summary("No json config files to test.") 119 120 def test_python_configs(self): 121 """Currently only "will this give me a config dictionary?" """ 122 config_files = self.query_config_files() 123 filecount = [0, 0] 124 for config_file in config_files: 125 if config_file.endswith(".py"): 126 filecount[0] += 1 127 self.info("Testing %s." % config_file) 128 global_dict = {} 129 local_dict = {} 130 try: 131 with open(config_file, "r") as f: 132 exec(f.read(), global_dict, local_dict) 133 except Exception: 134 self.add_summary( 135 "%s is invalid python." % config_file, level="error" 136 ) 137 self.error(pprint.pformat(sys.exc_info()[1])) 138 else: 139 if "config" in local_dict and isinstance( 140 local_dict["config"], dict 141 ): 142 self.info("Good.") 143 filecount[1] += 1 144 else: 145 self.add_summary( 146 "%s is valid python, " 147 "but doesn't create a config dictionary." % config_file, 148 level="error", 149 ) 150 if filecount[0]: 151 self.add_summary( 152 "%d of %d python config files were good." % (filecount[1], filecount[0]) 153 ) 154 else: 155 self.add_summary("No python config files to test.") 156 157 158# __main__ {{{1 159if __name__ == "__main__": 160 config_test = ConfigTest() 161 config_test.run_and_exit() 162