LocalDataStorage.cs 5.8 KB
Newer Older
1 2 3 4
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
J
Jared Parsons 已提交
5
using System.IO;
6 7 8 9
using System.Linq;
using System.Text;
using System.Threading.Tasks;

J
Jared Parsons 已提交
10
namespace RunTests.Cache
11 12 13
{
    /// <summary>
    /// Data storage that works under %LOCALAPPDATA%
J
Jared Parsons 已提交
14
    /// TODO: need to do garbage collection on the files
15 16 17
    /// </summary>
    internal sealed class LocalDataStorage : IDataStorage
    {
J
Jared Parsons 已提交
18 19 20 21 22
        private enum StorageKind
        {
            ExitCode,
            StandardOutput,
            ErrorOutput,
23 24
            ResultsFileContent,
            ResultsFileName,
J
Jared Parsons 已提交
25
            EllapsedSeconds,
J
Jared Parsons 已提交
26
            Content
J
Jared Parsons 已提交
27 28
        }

J
Jared Parsons 已提交
29
        internal const int MaxStorageCount = 200;
J
Jared Parsons 已提交
30 31 32 33
        internal const string DirectoryName = "RunTestsStorage";

        private readonly string _storagePath;

34 35
        public string Name => "local";

J
Jared Parsons 已提交
36 37 38 39 40
        internal LocalDataStorage(string storagePath = null)
        {
            _storagePath = storagePath ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), DirectoryName);
        }

J
Jared Parsons 已提交
41
        public Task<CachedTestResult?> TryGetCachedTestResult(string checksum)
42
        {
J
Jared Parsons 已提交
43 44 45 46 47 48 49 50 51 52 53 54
            CachedTestResult testResult;
            CachedTestResult? value = null;
            if (TryGetCachedTestResult(checksum, out testResult))
            {
                value = testResult;
            }

            return Task.FromResult(value);
        }

        public bool TryGetCachedTestResult(string checksum, out CachedTestResult testResult)
        { 
55
            testResult = default(CachedTestResult);
J
Jared Parsons 已提交
56

J
Jared Parsons 已提交
57
            var storageFolder = GetStorageFolder(checksum);
J
Jared Parsons 已提交
58 59 60 61 62
            if (!Directory.Exists(storageFolder))
            {
                return false;
            }

J
Jared Parsons 已提交
63 64
            try
            {
J
Jared Parsons 已提交
65 66 67
                var exitCode = Read(checksum, StorageKind.ExitCode);
                var standardOutput = Read(checksum, StorageKind.StandardOutput);
                var errorOutput = Read(checksum, StorageKind.ErrorOutput);
68 69
                var resultsFileName = Read(checksum, StorageKind.ResultsFileName);
                var resultsFileContent = Read(checksum, StorageKind.ResultsFileContent);
J
Jared Parsons 已提交
70
                var ellapsed = Read(checksum, StorageKind.EllapsedSeconds);
71

72
                testResult = new CachedTestResult(
J
Jared Parsons 已提交
73 74
                    exitCode: int.Parse(exitCode),
                    standardOutput: standardOutput,
75 76
                    errorOutput: errorOutput,
                    resultsFileName: resultsFileName,
J
Jared Parsons 已提交
77 78
                    resultsFileContent: resultsFileContent,
                    ellapsed: TimeSpan.FromSeconds(int.Parse(ellapsed)));
J
Jared Parsons 已提交
79
                return true;
J
Jared Parsons 已提交
80
            }
J
Jared Parsons 已提交
81
            catch (Exception e)
J
Jared Parsons 已提交
82 83
            {
                // Okay for exception to occur here on I/O
J
Jared Parsons 已提交
84
                Logger.Log($"Failed to read cache {checksum} {e.Message}");
J
Jared Parsons 已提交
85 86
            }

87 88 89
            return false;
        }

J
Jared Parsons 已提交
90
        public Task AddCachedTestResult(string assemblyName, ContentFile contentFile, CachedTestResult testResult)
91
        {
J
Jared Parsons 已提交
92 93
            var checksum = contentFile.Checksum;
            var storagePath = Path.Combine(_storagePath, checksum);
J
Jared Parsons 已提交
94
            try
J
Jared Parsons 已提交
95
            {
J
Jared Parsons 已提交
96
                if (!FileUtil.EnsureDirectory(storagePath))
J
Jared Parsons 已提交
97
                {
J
Jared Parsons 已提交
98
                    return Task.FromResult(true);
J
Jared Parsons 已提交
99
                }
J
Jared Parsons 已提交
100

J
Jared Parsons 已提交
101 102 103
                Write(checksum, StorageKind.ExitCode, testResult.ExitCode.ToString());
                Write(checksum, StorageKind.StandardOutput, testResult.StandardOutput);
                Write(checksum, StorageKind.ErrorOutput, testResult.ErrorOutput);
104 105
                Write(checksum, StorageKind.ResultsFileName, testResult.ResultsFileName);
                Write(checksum, StorageKind.ResultsFileContent, testResult.ResultsFileContent);
J
Jared Parsons 已提交
106
                Write(checksum, StorageKind.EllapsedSeconds, testResult.Ellapsed.TotalSeconds.ToString());
J
Jared Parsons 已提交
107
                Write(checksum, StorageKind.Content, contentFile.Content);
J
Jared Parsons 已提交
108
            }
J
Jared Parsons 已提交
109 110 111
            catch (Exception e)
            {
                // I/O errors are expected and okay here.
J
Jared Parsons 已提交
112
                Logger.Log($"Failed to log {checksum} {e.Message}");
J
Jared Parsons 已提交
113 114
                FileUtil.DeleteDirectory(storagePath);
            }
J
Jared Parsons 已提交
115 116

            return Task.FromResult(true);
J
Jared Parsons 已提交
117 118
        }

J
Jared Parsons 已提交
119
        private string GetStorageFolder(string checksum)
J
Jared Parsons 已提交
120
        {
J
Jared Parsons 已提交
121
            return Path.Combine(_storagePath, checksum);
J
Jared Parsons 已提交
122 123
        }

J
Jared Parsons 已提交
124
        private string GetStoragePath(string checksum, StorageKind kind)
J
Jared Parsons 已提交
125
        {
J
Jared Parsons 已提交
126
            return Path.Combine(GetStorageFolder(checksum), kind.ToString());
J
Jared Parsons 已提交
127 128
        }

J
Jared Parsons 已提交
129
        private void Write(string checksum, StorageKind kind, string contents)
J
Jared Parsons 已提交
130
        {
J
Jared Parsons 已提交
131
            var filePath = GetStoragePath(checksum, kind);
J
Jared Parsons 已提交
132 133 134
            File.WriteAllText(filePath, contents);
        }

J
Jared Parsons 已提交
135
        private string Read(string checksum, StorageKind kind)
J
Jared Parsons 已提交
136
        {
J
Jared Parsons 已提交
137
            var filePath = GetStoragePath(checksum, kind);
J
Jared Parsons 已提交
138
            return File.ReadAllText(filePath);
139
        }
J
Jared Parsons 已提交
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

        private void CleanupStorage()
        {
            try
            {
                var files = Directory.GetFiles(_storagePath);
                if (files.Length < MaxStorageCount)
                {
                    return;
                }

                var clean = files.Length - (MaxStorageCount / 2);
                var items = files
                    .Select(x => new DirectoryInfo(x))
                    .OrderBy(x => x.CreationTimeUtc)
                    .Take(clean);

                foreach (var item in items)
                {
                    FileUtil.DeleteDirectory(item.Name);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Unable to cleanup storage {ex.Message}");
            }
        }
167 168
    }
}