bash_test.sh (9121B)
1 #!/bin/bash 2 # Copyright (c) the JPEG XL Project Authors. All rights reserved. 3 # 4 # Use of this source code is governed by a BSD-style 5 # license that can be found in the LICENSE file. 6 7 # Tests implemented in bash. These typically will run checks about the source 8 # code rather than the compiled one. 9 10 SELF=$(realpath "$0") 11 MYDIR=$(dirname "${SELF}") 12 13 set -u 14 15 test_includes() { 16 local ret=0 17 local f 18 for f in $(git ls-files | grep -E '(\.cc|\.cpp|\.h)$'); do 19 if [ ! -e "$f" ]; then 20 continue 21 fi 22 # Check that the full paths to the public headers are not used, since users 23 # of the library will include the library as: #include "jxl/foobar.h". 24 if grep -i -H -n -E '#include\s*[<"]lib/include/jxl' "$f" >&2; then 25 echo "Don't add \"include/\" to the include path of public headers." >&2 26 ret=1 27 fi 28 29 if [[ "${f#third_party/}" == "$f" ]]; then 30 # $f is not in third_party/ 31 32 # Check that local files don't use the full path to third_party/ 33 # directory since the installed versions will not have that path. 34 # Add an exception for third_party/dirent.h. 35 if grep -v -F 'third_party/dirent.h' "$f" | \ 36 grep -i -H -n -E '#include\s*[<"]third_party/' >&2 && 37 [[ $ret -eq 0 ]]; then 38 cat >&2 <<EOF 39 $f: Don't add third_party/ to the include path of third_party projects. This \ 40 makes it harder to use installed system libraries instead of the third_party/ \ 41 ones. 42 EOF 43 ret=1 44 fi 45 fi 46 47 done 48 return ${ret} 49 } 50 51 test_include_collision() { 52 local ret=0 53 local f 54 for f in $(git ls-files | grep -E '^lib/include/'); do 55 if [ ! -e "$f" ]; then 56 continue 57 fi 58 local base=${f#lib/include/} 59 if [[ -e "lib/${base}" ]]; then 60 echo "$f: Name collision, both $f and lib/${base} exist." >&2 61 ret=1 62 fi 63 done 64 return ${ret} 65 } 66 67 test_copyright() { 68 local ret=0 69 local f 70 for f in $( 71 git ls-files | grep -E \ 72 '(Dockerfile.*|\.c|\.cc|\.cpp|\.gni|\.h|\.java|\.sh|\.m|\.py|\.ui|\.yml)$'); do 73 if [ ! -e "$f" ]; then 74 continue 75 fi 76 if [[ "${f#third_party/}" == "$f" ]]; then 77 # $f is not in third_party/ 78 if ! head -n 10 "$f" | 79 grep -F 'Copyright (c) the JPEG XL Project Authors.' >/dev/null ; then 80 echo "$f: Missing Copyright blob near the top of the file." >&2 81 ret=1 82 fi 83 if ! head -n 10 "$f" | 84 grep -F 'Use of this source code is governed by a BSD-style' \ 85 >/dev/null ; then 86 echo "$f: Missing License blob near the top of the file." >&2 87 ret=1 88 fi 89 fi 90 done 91 return ${ret} 92 } 93 94 # Check that we don't use "%zu" or "%zd" in format string for size_t. 95 test_printf_size_t() { 96 local ret=0 97 if grep -n -E '%[0-9]*z[udx]' \ 98 $(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$'); then 99 echo "Don't use '%zu' or '%zd' in a format string, instead use " \ 100 "'%\" PRIuS \"' or '%\" PRIdS \"'." >&2 101 ret=1 102 fi 103 104 if grep -n -E '[^_]gtest\.h' \ 105 $(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$' | grep -v -F /testing.h); then 106 echo "Don't include gtest directly, instead include 'testing.h'. " >&2 107 ret=1 108 fi 109 110 local f 111 for f in $(git ls-files | grep -E "\.cc$" | xargs grep 'PRI[udx]S' | 112 cut -f 1 -d : | uniq); do 113 if [ ! -e "$f" ]; then 114 continue 115 fi 116 if ! grep -F printf_macros.h "$f" >/dev/null; then 117 echo "$f: Add lib/jxl/base/printf_macros.h for PRI.S, or use other " \ 118 "types for code outside lib/jxl library." >&2 119 ret=1 120 fi 121 done 122 123 for f in $(git ls-files | grep -E "\.h$" | grep -v -E '(printf_macros\.h|testing\.h)' | 124 xargs grep -n 'PRI[udx]S'); do 125 # Having PRIuS / PRIdS in a header file means that printf_macros.h may 126 # be included before a system header, in particular before gtest headers. 127 # those may re-define PRIuS unconditionally causing a compile error. 128 echo "$f: Don't use PRI.S in header files. Sorry." 129 ret=1 130 done 131 132 return ${ret} 133 } 134 135 # Check that "dec_" code doesn't depend on "enc_" headers. 136 test_dec_enc_deps() { 137 local ret=0 138 local f 139 for f in $(git ls-files | grep -E '/dec_'); do 140 if [ ! -e "$f" ]; then 141 continue 142 fi 143 if [[ "${f#third_party/}" == "$f" ]]; then 144 # $f is not in third_party/ 145 if grep -n -H -E "#include.*/enc_" "$f" >&2; then 146 echo "$f: Don't include \"enc_*\" files from \"dec_*\" files." >&2 147 ret=1 148 fi 149 fi 150 done 151 return ${ret} 152 } 153 154 # Check for git merge conflict markers. 155 test_merge_conflict() { 156 local ret=0 157 TEXT_FILES='(\.cc|\.cpp|\.h|\.sh|\.m|\.py|\.md|\.txt|\.cmake)$' 158 for f in $(git ls-files | grep -E "${TEXT_FILES}"); do 159 if [ ! -e "$f" ]; then 160 continue 161 fi 162 if grep -E '^<<<<<<< ' "$f"; then 163 echo "$f: Found git merge conflict marker. Please resolve." >&2 164 ret=1 165 fi 166 done 167 return ${ret} 168 } 169 170 # Check that the library and the package have the same version. This prevents 171 # accidentally having them out of sync. 172 get_version() { 173 local varname=$1 174 local line=$(grep -F "set(${varname} " lib/CMakeLists.txt | head -n 1) 175 [[ -n "${line}" ]] 176 line="${line#set(${varname} }" 177 line="${line%)}" 178 echo "${line}" 179 } 180 181 test_version() { 182 local major=$(get_version JPEGXL_MAJOR_VERSION) 183 local minor=$(get_version JPEGXL_MINOR_VERSION) 184 local patch=$(get_version JPEGXL_PATCH_VERSION) 185 # Check that the version is not empty 186 if [[ -z "${major}${minor}${patch}" ]]; then 187 echo "Couldn't parse version from CMakeLists.txt" >&2 188 return 1 189 fi 190 local pkg_version=$(head -n 1 debian/changelog) 191 # Get only the part between the first "jpeg-xl (" and the following ")". 192 pkg_version="${pkg_version#jpeg-xl (}" 193 pkg_version="${pkg_version%%)*}" 194 if [[ -z "${pkg_version}" ]]; then 195 echo "Couldn't parse version from debian package" >&2 196 return 1 197 fi 198 199 local lib_version="${major}.${minor}.${patch}" 200 lib_version="${lib_version%.0}" 201 if [[ "${pkg_version}" != "${lib_version}"* ]]; then 202 echo "Debian package version (${pkg_version}) doesn't match library" \ 203 "version (${lib_version})." >&2 204 return 1 205 fi 206 return 0 207 } 208 209 # Check that the SHA versions in deps.sh matches the git submodules. 210 test_deps_version() { 211 while IFS= read -r line; do 212 if [[ "${line:0:10}" != "[submodule" ]]; then 213 continue 214 fi 215 line="${line#[submodule \"}" 216 line="${line%\"]}" 217 local varname=$(tr '[:lower:]' '[:upper:]' <<< "${line}") 218 varname="${varname/\//_}" 219 if ! grep -F "${varname}=" deps.sh >/dev/null; then 220 # Ignoring submodule not in deps.sh 221 continue 222 fi 223 local deps_sha=$(grep -F "${varname}=" deps.sh | cut -f 2 -d '"') 224 [[ -n "${deps_sha}" ]] 225 local git_sha=$(git ls-tree -r HEAD "${line}" | cut -f 1 | cut -f 3 -d ' ') 226 if [[ "${deps_sha}" != "${git_sha}" ]]; then 227 cat >&2 <<EOF 228 deps.sh: SHA for project ${line} is at ${deps_sha} but the git submodule is at 229 ${git_sha}. Please update deps.sh 230 231 If you did not intend to change the submodule's SHA value, it is possible that 232 you accidentally included this change in your commit after a rebase or checkout 233 without running "git submodule --init". To revert the submodule change run from 234 the top checkout directory: 235 236 git -C ${line} checkout ${deps_sha} 237 git commit --amend ${line} 238 239 EOF 240 return 1 241 fi 242 done < .gitmodules 243 } 244 245 # Make sure that all the Fields objects are fuzzed directly. 246 test_fuzz_fields() { 247 local ret=0 248 # List all the classes of the form "ClassName : public Fields". 249 # This doesn't catch class names that are too long to fit. 250 local field_classes=$( git ls-files | 251 grep -E '\.(cc|h)' | grep -v 'test\.cc$' | 252 xargs grep -h -o -E '\b[^ ]+ : public Fields' | cut -f 1 -d ' ') 253 local classname 254 for classname in ${field_classes}; do 255 if [ ! -e "$classname" ]; then 256 continue 257 fi 258 if ! grep -E "\\b${classname}\\b" tools/fields_fuzzer.cc >/dev/null; then 259 cat >&2 <<EOF 260 tools/fields_fuzzer.cc: Class ${classname} not found in the fields_fuzzer. 261 EOF 262 ret=1 263 fi 264 done 265 return $ret 266 } 267 268 # Test that we don't use %n in C++ code to avoid using it in printf and scanf. 269 # This test is not very precise but in cases where "module n" is needed we would 270 # normally have "% n" instead of "%n". Using %n is not allowed in Android 10+. 271 test_percent_n() { 272 local ret=0 273 local f 274 for f in $(git ls-files | grep -E '(\.cc|\.cpp|\.h)$'); do 275 if [ ! -e "$f" ]; then 276 continue 277 fi 278 if grep -i -H -n -E '%h*n' "$f" >&2; then 279 echo "Don't use \"%n\"." >&2 280 ret=1 281 fi 282 done 283 return ${ret} 284 } 285 286 main() { 287 local ret=0 288 cd "${MYDIR}" 289 290 if ! git rev-parse >/dev/null 2>/dev/null; then 291 echo "Not a git checkout, skipping bash_test" 292 return 0 293 fi 294 295 IFS=$'\n' 296 for f in $(declare -F); do 297 local test_name=$(echo "$f" | cut -f 3 -d ' ') 298 # Runs all the local bash functions that start with "test_". 299 if [[ "${test_name}" == test_* ]]; then 300 echo "Test ${test_name}: Start" 301 if ${test_name}; then 302 echo "Test ${test_name}: PASS" 303 else 304 echo "Test ${test_name}: FAIL" 305 ret=1 306 fi 307 fi 308 done 309 return ${ret} 310 } 311 312 main "$@"