1#!/usr/bin/python 2# 3# Copyright 2015 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7import codecs 8import copy 9import credits_updater as cu 10import os 11import string 12import unittest 13 14# Assumes this script is in ffmpeg/chromium/scripts/ 15SOURCE_DIR = os.path.join( 16 os.path.dirname(os.path.abspath(__file__)), os.path.pardir, os.path.pardir) 17OUTPUT_FILE = 'CREDITS.testing' 18 19# Expected credits for swresample.h applied with the rot13 encoding. Otherwise 20# license scanners get confused about the license of this file. 21SWRESAMPLE_H_LICENSE_ROT_13 = """yvofjerfnzcyr/fjerfnzcyr.u 22 23Pbclevtug (P) 2011-2013 Zvpunry Avrqreznlre (zvpunryav@tzk.ng) 24 25Guvf svyr vf cneg bs yvofjerfnzcyr 26 27yvofjerfnzcyr vf serr fbsgjner; lbh pna erqvfgevohgr vg naq/be 28zbqvsl vg haqre gur grezf bs gur TAH Yrffre Trareny Choyvp 29Yvprafr nf choyvfurq ol gur Serr Fbsgjner Sbhaqngvba; rvgure 30irefvba 2.1 bs gur Yvprafr, be (ng lbhe bcgvba) nal yngre irefvba. 31 32yvofjerfnzcyr vf qvfgevohgrq va gur ubcr gung vg jvyy or hfrshy, 33ohg JVGUBHG NAL JNEENAGL; jvgubhg rira gur vzcyvrq jneenagl bs 34ZREPUNAGNOVYVGL be SVGARFF SBE N CNEGVPHYNE CHECBFR. Frr gur TAH 35Yrffre Trareny Choyvp Yvprafr sbe zber qrgnvyf. 36 37Lbh fubhyq unir erprvirq n pbcl bs gur TAH Yrffre Trareny Choyvp 38Yvprafr nybat jvgu yvofjerfnzcyr; vs abg, jevgr gb gur Serr Fbsgjner 39Sbhaqngvba, Vap., 51 Senaxyva Fgerrg, Svsgu Sybbe, Obfgba, ZN 02110-1301 HFN""" 40 41# The real expected credits for swresample.h. 42SWRESAMPLE_H_LICENSE = codecs.decode(SWRESAMPLE_H_LICENSE_ROT_13, 'rot13') 43 44 45def NewCreditsUpdater(): 46 return cu.CreditsUpdater(SOURCE_DIR, OUTPUT_FILE) 47 48 49class CreditsUpdaterUnittest(unittest.TestCase): 50 51 def tearDown(self): 52 # Cleanup the testing output file 53 test_credits = os.path.join(SOURCE_DIR, OUTPUT_FILE) 54 if os.path.exists(test_credits): 55 os.remove(test_credits) 56 57 def testNoFiles(self): 58 # Write credits without processing any files. 59 NewCreditsUpdater().WriteCredits() 60 61 # Credits should *always* have LICENSE.md followed by full LGPL text. 62 expected_lines = NormalizeNewLines(GetLicenseMdLines() + 63 GetSeparatorLines() + 64 GetLicenseLines(cu.License.LGPL)) 65 credits_lines = ReadCreditsLines() 66 self.assertEqual(expected_lines, credits_lines) 67 68 def testLPGLFiles(self): 69 # Process two known LGPL files 70 updater = NewCreditsUpdater() 71 updater.ProcessFile('libavformat/mp3dec.c') 72 updater.ProcessFile('libavformat/mp3enc.c') 73 updater.WriteCredits() 74 75 # Expect output to have just LGPL text (once) preceded by LICENSE.md 76 expected_lines = NormalizeNewLines(GetLicenseMdLines() + 77 GetSeparatorLines() + 78 GetLicenseLines(cu.License.LGPL)) 79 credits_lines = ReadCreditsLines() 80 self.assertEqual(expected_lines, credits_lines) 81 82 def testKnownBucketFiles(self): 83 # Process some JPEG and MIPS files. 84 updater = NewCreditsUpdater() 85 updater.ProcessFile('libavcodec/jfdctfst.c') 86 updater.ProcessFile('libavutil/mips/float_dsp_mips.c') 87 updater.WriteCredits() 88 89 # Expected output to have JPEG and MIPS text in addition to the typical LGPL 90 # and LICENSE.md header. JPEG should appear before MIPS because known 91 # buckets will be printed in alphabetical order. 92 expected_lines = NormalizeNewLines( 93 GetLicenseMdLines() + GetSeparatorLines() + 94 ['libavcodec/jfdctfst.c\n\n'] + GetLicenseLines(cu.License.JPEG) + 95 GetSeparatorLines() + ['libavutil/mips/float_dsp_mips.c\n\n'] + 96 GetLicenseLines(cu.License.MIPS) + GetSeparatorLines() + 97 GetLicenseLines(cu.License.LGPL)) 98 credits_lines = ReadCreditsLines() 99 self.assertEqual(expected_lines, credits_lines) 100 101 def testGeneratedAndKnownLicences(self): 102 # Process a file that doesn't fall into a known bucket (e.g. the license 103 # header for this file is unique). Also process a known bucket file. 104 updater = NewCreditsUpdater() 105 updater.ProcessFile('libswresample/swresample.h') 106 updater.ProcessFile('libavutil/mips/float_dsp_mips.c') 107 updater.WriteCredits() 108 109 # Expect output to put swresample.h header first, followed by MIPS. 110 expected_lines = NormalizeNewLines( 111 GetLicenseMdLines() + GetSeparatorLines() + 112 SWRESAMPLE_H_LICENSE.splitlines(True) + GetSeparatorLines() + 113 ['libavutil/mips/float_dsp_mips.c\n\n'] + 114 GetLicenseLines(cu.License.MIPS) + GetSeparatorLines() + 115 GetLicenseLines(cu.License.LGPL)) 116 credits_lines = ReadCreditsLines() 117 self.assertEqual(expected_lines, credits_lines) 118 119 def testGeneratedLicencesOrder(self): 120 # Process files that do not fall into a known bucket and assert that their 121 # licenses are listed in alphabetical order of the file names. 122 files = [ 123 'libswresample/swresample.h', 124 'libavcodec/arm/jrevdct_arm.S', 125 'libavcodec/mips/celp_math_mips.c', 126 'libavcodec/mips/acelp_vectors_mips.c', 127 'libavformat/oggparsetheora.c', 128 'libavcodec/x86/xvididct.asm', 129 ] 130 updater = NewCreditsUpdater() 131 for f in files: 132 updater.ProcessFile(f) 133 updater.WriteCredits() 134 135 credits = ''.join(ReadCreditsLines()) 136 current_offset = 0 137 for f in sorted(files): 138 i = string.find(credits, f, current_offset) 139 if i == -1: 140 self.fail("Failed to find %s starting at offset %s of content:\n%s" % 141 (f, current_offset, credits)) 142 current_offset = i + len(f) 143 144 145 def testKnownFileDigestChange(self): 146 updater = NewCreditsUpdater() 147 148 # Choose a known file. 149 known_file = os.path.join('libavformat', 'oggparseogm.c') 150 self.assertTrue(known_file in updater.known_file_map) 151 152 # Show file processing works without raising SystemExit. 153 updater.ProcessFile(known_file) 154 155 # Alter the license digest for this file to simulate a change to the 156 # license header. 157 orig_file_info = updater.known_file_map[known_file] 158 altered_file_info = cu.FileInfo(cu.License.LGPL, 159 'chris' + orig_file_info.license_digest[5:]) 160 updater.known_file_map[known_file] = altered_file_info 161 162 # Verify digest mismatch triggers SystemExit. 163 with self.assertRaises(SystemExit): 164 updater.ProcessFile(known_file) 165 166 167# Globals to cache the text of static files once read. 168g_license_md_lines = [] 169g_license_lines = {} 170 171 172def ReadCreditsLines(): 173 with open(os.path.join(SOURCE_DIR, OUTPUT_FILE)) as test_credits: 174 return test_credits.readlines() 175 176 177def GetLicenseMdLines(): 178 global g_license_md_lines 179 if not len(g_license_md_lines): 180 with open(os.path.join(SOURCE_DIR, cu.UPSTREAM_LICENSEMD)) as license_md: 181 g_license_md_lines = license_md.readlines() 182 return g_license_md_lines 183 184 185def GetLicenseLines(license_file): 186 if not license_file in g_license_lines: 187 g_license_lines[license_file] = GetFileLines( 188 os.path.join(cu.LICENSE_TEXTS[license_file])) 189 return g_license_lines[license_file] 190 191 192def GetFileLines(file_path): 193 with open(file_path) as open_file: 194 return open_file.readlines() 195 196 197def GetSeparatorLines(): 198 # Pass True to preserve \n chars in the return. 199 return cu.LICENSE_SEPARATOR.splitlines(True) 200 201 202# Combine into a string then split back out to a list. This is important for 203# making constructed expectations match the credits read from a file. E.g. 204# input: ['foo', '\n', 'bar'] 205# return: ['foo\n', 'bar'] 206# Comparing lists line by line makes for much better diffs when things go wrong. 207 208 209def NormalizeNewLines(lines): 210 return ''.join(lines).splitlines(True) 211 212 213if __name__ == '__main__': 214 unittest.main() 215