utils_unittest.py revision 010c0bcc9c7d91c451534d95b12305649627c180
1#!/usr/bin/python 2# Copyright 2017 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""unittest for utils.py 7""" 8 9import json 10import os 11import shutil 12import tempfile 13import time 14import unittest 15 16import common 17from autotest_lib.client.bin.result_tools import utils as result_utils 18from autotest_lib.client.bin.result_tools import view as result_view 19 20SIZE = 10 21EXPECTED_SUMMARY = { 22 '': {result_utils.ORIGINAL_SIZE_BYTES: 4 * SIZE, 23 result_utils.DIRS: { 24 'file1': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}, 25 'folder1': {result_utils.ORIGINAL_SIZE_BYTES: 2 * SIZE, 26 result_utils.DIRS: { 27 'file2': { 28 result_utils.ORIGINAL_SIZE_BYTES: SIZE}, 29 'file3': { 30 result_utils.ORIGINAL_SIZE_BYTES: SIZE}, 31 'symlink': { 32 result_utils.ORIGINAL_SIZE_BYTES: 0, 33 result_utils.DIRS: {}}}}, 34 'folder2': {result_utils.ORIGINAL_SIZE_BYTES: SIZE, 35 result_utils.DIRS: 36 {'file2': 37 {result_utils.ORIGINAL_SIZE_BYTES: 38 SIZE}}, 39 }}}} 40 41SUMMARY_1 = { 42 '': {result_utils.ORIGINAL_SIZE_BYTES: 4 * SIZE, 43 result_utils.DIRS: { 44 'file1': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}, 45 'file2': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}, 46 'folder_not_overwritten': 47 {result_utils.ORIGINAL_SIZE_BYTES: SIZE, 48 result_utils.DIRS: { 49 'file1': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}} 50 }, 51 'file_to_be_overwritten': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}, 52 } 53 } 54 } 55 56SUMMARY_2 = { 57 '': {result_utils.ORIGINAL_SIZE_BYTES: 26 * SIZE, 58 result_utils.DIRS: { 59 # `file1` exists and has the same size. 60 'file1': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}, 61 # Change the size of `file2` to make sure summary merge works. 62 'file2': {result_utils.ORIGINAL_SIZE_BYTES: 2 * SIZE}, 63 # `file3` is new. 64 'file3': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}, 65 # Add a new sub-directory. 66 'folder1': {result_utils.ORIGINAL_SIZE_BYTES: 20 * SIZE, 67 result_utils.TRIMMED_SIZE_BYTES: SIZE, 68 result_utils.DIRS: { 69 # Add a file being trimmed. 70 'file4': { 71 result_utils.ORIGINAL_SIZE_BYTES: 20 * SIZE, 72 result_utils.TRIMMED_SIZE_BYTES: SIZE} 73 } 74 }, 75 # Add a file whose name collides with the previous summary. 76 'folder_not_overwritten': { 77 result_utils.ORIGINAL_SIZE_BYTES: 100 * SIZE}, 78 # Add a directory whose name collides with the previous summary. 79 'file_to_be_overwritten': 80 {result_utils.ORIGINAL_SIZE_BYTES: SIZE, 81 result_utils.DIRS: { 82 'file1': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}} 83 }, 84 # Folder was collected, not missing from the final result folder. 85 'folder_tobe_deleted': 86 {result_utils.ORIGINAL_SIZE_BYTES: SIZE, 87 result_utils.DIRS: { 88 'file_tobe_deleted': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}} 89 }, 90 } 91 } 92 } 93 94SUMMARY_1_SIZE = 171 95SUMMARY_2_SIZE = 345 96 97# The final result dir has an extra folder and file, also with `file3` removed 98# to test the case that client files are removed on the server side. 99EXPECTED_MERGED_SUMMARY = { 100 '': {result_utils.ORIGINAL_SIZE_BYTES: 101 37 * SIZE + SUMMARY_1_SIZE + SUMMARY_2_SIZE, 102 result_utils.TRIMMED_SIZE_BYTES: 103 17 * SIZE + SUMMARY_1_SIZE + SUMMARY_2_SIZE, 104 # Size collected is SIZE bytes more than total size as an old `file2` of 105 # SIZE bytes is overwritten by a newer file. 106 result_utils.COLLECTED_SIZE_BYTES: 107 19 * SIZE + SUMMARY_1_SIZE + SUMMARY_2_SIZE, 108 result_utils.DIRS: { 109 'dir_summary_1.json': { 110 result_utils.ORIGINAL_SIZE_BYTES: SUMMARY_1_SIZE}, 111 'dir_summary_2.json': { 112 result_utils.ORIGINAL_SIZE_BYTES: SUMMARY_2_SIZE}, 113 'file1': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}, 114 'file2': {result_utils.ORIGINAL_SIZE_BYTES: 2 * SIZE, 115 result_utils.COLLECTED_SIZE_BYTES: 3 * SIZE, 116 result_utils.TRIMMED_SIZE_BYTES: 2 * SIZE}, 117 'file3': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}, 118 'folder1': {result_utils.ORIGINAL_SIZE_BYTES: 20 * SIZE, 119 result_utils.TRIMMED_SIZE_BYTES: SIZE, 120 result_utils.COLLECTED_SIZE_BYTES: SIZE, 121 result_utils.DIRS: { 122 'file4': {result_utils.ORIGINAL_SIZE_BYTES: 20 * SIZE, 123 result_utils.TRIMMED_SIZE_BYTES: SIZE} 124 } 125 }, 126 'folder2': {result_utils.ORIGINAL_SIZE_BYTES: 10 * SIZE, 127 result_utils.COLLECTED_SIZE_BYTES: 10 * SIZE, 128 result_utils.TRIMMED_SIZE_BYTES: 10 * SIZE, 129 result_utils.DIRS: { 130 'server_file': { 131 result_utils.ORIGINAL_SIZE_BYTES: 10 * SIZE} 132 } 133 }, 134 'folder_not_overwritten': 135 {result_utils.ORIGINAL_SIZE_BYTES: SIZE, 136 result_utils.COLLECTED_SIZE_BYTES: SIZE, 137 result_utils.TRIMMED_SIZE_BYTES: SIZE, 138 result_utils.DIRS: { 139 'file1': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}} 140 }, 141 'file_to_be_overwritten': 142 {result_utils.ORIGINAL_SIZE_BYTES: SIZE, 143 result_utils.COLLECTED_SIZE_BYTES: SIZE, 144 result_utils.TRIMMED_SIZE_BYTES: SIZE, 145 result_utils.DIRS: { 146 'file1': {result_utils.ORIGINAL_SIZE_BYTES: SIZE}} 147 }, 148 'folder_tobe_deleted': 149 {result_utils.ORIGINAL_SIZE_BYTES: SIZE, 150 result_utils.COLLECTED_SIZE_BYTES: SIZE, 151 result_utils.TRIMMED_SIZE_BYTES: 0, 152 result_utils.DIRS: { 153 'file_tobe_deleted': {result_utils.ORIGINAL_SIZE_BYTES: SIZE, 154 result_utils.COLLECTED_SIZE_BYTES: SIZE, 155 result_utils.TRIMMED_SIZE_BYTES: 0}} 156 }, 157 } 158 } 159 } 160 161def create_file(path, size=SIZE): 162 """Create a temp file at given path with the given size. 163 164 @param path: Path to the temp file. 165 @param size: Size of the temp file, default to SIZE. 166 """ 167 with open(path, 'w') as f: 168 f.write('A' * size) 169 170 171class GetDirSummaryTest(unittest.TestCase): 172 """Test class for get_dir_summary method""" 173 174 def setUp(self): 175 """Setup directory for test.""" 176 self.test_dir = tempfile.mkdtemp() 177 file1 = os.path.join(self.test_dir, 'file1') 178 create_file(file1) 179 folder1 = os.path.join(self.test_dir, 'folder1') 180 os.mkdir(folder1) 181 file2 = os.path.join(folder1, 'file2') 182 create_file(file2) 183 file3 = os.path.join(folder1, 'file3') 184 create_file(file3) 185 186 folder2 = os.path.join(self.test_dir, 'folder2') 187 os.mkdir(folder2) 188 file4 = os.path.join(folder2, 'file2') 189 create_file(file4) 190 191 symlink = os.path.join(folder1, 'symlink') 192 os.symlink(folder2, symlink) 193 194 def tearDown(self): 195 """Cleanup the test directory.""" 196 shutil.rmtree(self.test_dir, ignore_errors=True) 197 198 def test_get_dir_summary(self): 199 """Test method get_dir_summary.""" 200 summary_json = result_utils.get_dir_summary( 201 self.test_dir + '/', self.test_dir + '/') 202 self.assertEqual(EXPECTED_SUMMARY, summary_json) 203 204 205class MergeSummaryTest(unittest.TestCase): 206 """Test class for merge_summaries method""" 207 208 def setUp(self): 209 """Setup directory to match the file structure in MERGED_SUMMARY.""" 210 self.test_dir = tempfile.mkdtemp() + '/' 211 file1 = os.path.join(self.test_dir, 'file1') 212 create_file(file1) 213 file2 = os.path.join(self.test_dir, 'file2') 214 create_file(file2, 2*SIZE) 215 file3 = os.path.join(self.test_dir, 'file3') 216 create_file(file3, SIZE) 217 folder1 = os.path.join(self.test_dir, 'folder1') 218 os.mkdir(folder1) 219 file4 = os.path.join(folder1, 'file4') 220 create_file(file4, SIZE) 221 folder2 = os.path.join(self.test_dir, 'folder2') 222 os.mkdir(folder2) 223 server_file = os.path.join(folder2, 'server_file') 224 create_file(server_file, 10*SIZE) 225 folder_not_overwritten = os.path.join( 226 self.test_dir, 'folder_not_overwritten') 227 os.mkdir(folder_not_overwritten) 228 file1 = os.path.join(folder_not_overwritten, 'file1') 229 create_file(file1) 230 file_to_be_overwritten = os.path.join( 231 self.test_dir, 'file_to_be_overwritten') 232 os.mkdir(file_to_be_overwritten) 233 file1 = os.path.join(file_to_be_overwritten, 'file1') 234 create_file(file1) 235 236 # Save summary file to test_dir 237 self.summary_1 = os.path.join(self.test_dir, 'dir_summary_1.json') 238 with open(self.summary_1, 'w') as f: 239 json.dump(SUMMARY_1, f) 240 # Wait for 10ms, to make sure summary_2 has a later time stamp. 241 time.sleep(0.01) 242 self.summary_2 = os.path.join(self.test_dir, 'dir_summary_2.json') 243 with open(self.summary_2, 'w') as f: 244 json.dump(SUMMARY_2, f) 245 246 def tearDown(self): 247 """Cleanup the test directory.""" 248 shutil.rmtree(self.test_dir, ignore_errors=True) 249 250 def testMergeSummaries(self): 251 """Test method merge_summaries.""" 252 client_collected_bytes, merged_summary = result_utils.merge_summaries( 253 self.test_dir) 254 self.assertEqual(EXPECTED_MERGED_SUMMARY, merged_summary) 255 self.assertEqual(client_collected_bytes, 9 * SIZE) 256 257 def testMergeSummariesFromNoHistory(self): 258 """Test method merge_summaries can handle results with no existing 259 summary. 260 """ 261 os.remove(self.summary_1) 262 os.remove(self.summary_2) 263 client_collected_bytes, _ = result_utils.merge_summaries(self.test_dir) 264 self.assertEqual(client_collected_bytes, 0) 265 266 def testBuildView(self): 267 """Test build method in result_view module.""" 268 client_collected_bytes, summary = result_utils.merge_summaries( 269 self.test_dir) 270 html_file = os.path.join(self.test_dir, 271 result_view.DEFAULT_RESULT_SUMMARY_NAME) 272 result_view.build(client_collected_bytes, summary, html_file) 273 # Make sure html_file is created with content. 274 self.assertGreater(os.stat(html_file).st_size, 1000) 275 276 277# this is so the test can be run in standalone mode 278if __name__ == '__main__': 279 """Main""" 280 unittest.main() 281