• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MeindertN / RoboClerk / 16417701102

21 Jul 2025 01:00PM UTC coverage: 77.312% (-3.0%) from 80.318%
16417701102

push

github

MeindertN
WIP: unit tests are passing

1649 of 2179 branches covered (75.68%)

Branch coverage included in aggregate %.

3 of 3 new or added lines in 2 files covered. (100.0%)

8 existing lines in 1 file now uncovered.

5374 of 6905 relevant lines covered (77.83%)

95.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.76
/RoboClerk/Program.cs
1
using CommandLine;
2
using DocumentFormat.OpenXml.Office2016.Drawing.ChartDrawing;
3
using Microsoft.Extensions.DependencyInjection;
4
using NLog;
5
using RoboClerk.AISystem;
6
using RoboClerk.Configuration;
7
using RoboClerk.ContentCreators;
8
using System;
9
using System.Collections.Generic;
10
using System.IO;
11
using System.IO.Abstractions;
12
using System.Linq;
13
using System.Reflection;
14
using System.Runtime.CompilerServices;
15
using Tomlyn;
16

17
[assembly: AssemblyVersion("2.0.*")]
18
[assembly: InternalsVisibleTo("RoboClerk.Tests")]
19

20
namespace RoboClerk
21
{
22
    class Program
23
    {
24
        internal static void ConfigureLogging(string configFile)
25
        {
4✔
26
            var toml = Toml.Parse(File.ReadAllText(configFile)).ToModel();
4✔
27
            var logLevel = (string)toml["LogLevel"];
3✔
28
            var outputDir = (string)toml["OutputDirectory"];
3✔
29

30
            var config = new NLog.Config.LoggingConfiguration();
3✔
31

32
            // Targets where to log to
33
            var logFile = new NLog.Targets.FileTarget("logfile")
3✔
34
            {
3✔
35
                FileName =
3✔
36
                $"{outputDir}{Path.DirectorySeparatorChar}RoboClerkLog.txt",
3✔
37
                DeleteOldFileOnStartup = true
3✔
38
            };
3✔
39
            Console.WriteLine(logFile.FileName);
3✔
40
            if (logLevel.ToUpper() == "DEBUG")
3✔
41
            {
1✔
42
                config.AddRule(LogLevel.Debug, LogLevel.Fatal, logFile);
1✔
43
            }
1✔
44
            else if (logLevel.ToUpper() == "WARN")
2✔
45
            {
1✔
46
                config.AddRule(LogLevel.Warn, LogLevel.Fatal, logFile);
1✔
47
            }
1✔
48
            else 
49
            {
1✔
50
                config.AddRule(LogLevel.Info, LogLevel.Fatal, logFile);
1✔
51
            }
1✔
52
            LogManager.Configuration = config;
3✔
53
        }
3✔
54

55
        internal static Dictionary<string, string> GetConfigOptions(IEnumerable<string> commandlineOptions, ILogger logger)
56
        {
4✔
57
            Dictionary<string, string> options = new Dictionary<string, string>();
4✔
58
            foreach (var commandlineOption in commandlineOptions)
31✔
59
            {
10✔
60
                if (commandlineOption != ",")
10✔
61
                {
8✔
62
                    var elements = commandlineOption.Split('=');
8✔
63
                    if (elements.Length != 2)
8✔
64
                    {
1✔
65
                        logger.Error($"Commandline option can not be parsed: {commandlineOption}. Please check commandline call, it should be in the form of <IDENTIFIER>=<VALUE>");
1✔
66
                        Console.WriteLine($"An error occurred parsing commandline option: {commandlineOption}. Expected syntax is <IDENTIFIER>=<VALUE>.");
1✔
67
                        throw new Exception("Error parsing commandline options.");
1✔
68
                    }
69
                    options[elements[0]] = elements[1];
7✔
70
                }
7✔
71
            }
9✔
72
            return options;
3✔
73
        }
3✔
74

75
        internal static void CleanOutputDirectory(string outputDir, ILogger logger)
76
        {
2✔
77
            logger.Info("Cleaning output directory.");
2✔
78
            string[] files = Directory.GetFiles(outputDir);
2✔
79
            foreach (string file in files)
13✔
80
            {
5✔
81
                if (!file.Contains("RoboClerkLog.txt") &&
5✔
82
                    !file.Contains(".gitignore"))
5✔
83
                {
3✔
84
                    File.Delete(file);
3✔
85
                }
3✔
86
            }
5✔
87
        }
1✔
88

89
        internal static void RegisterContentCreators(IServiceCollection services)
90
        {
2✔
91
            ArgumentNullException.ThrowIfNull(services);
2✔
92

93
            // Get the assembly containing the content creators            
94
            var currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
1✔
95
            var otherAssemblyPath = Path.Combine(currentDir, "RoboClerk.Core.dll");
1✔
96
            var assembly = Assembly.LoadFrom(otherAssemblyPath);
1✔
97

98
            // Find all types that implement IContentCreator
99
            var contentCreatorTypes = assembly.GetTypes()
1✔
100
                .Where(t => typeof(IContentCreator).IsAssignableFrom(t) && 
181✔
101
                           !t.IsInterface && 
181✔
102
                           !t.IsAbstract &&
181✔
103
                           !t.IsGenericType)
181✔
104
                .ToList();
1✔
105

106
            var logger = NLog.LogManager.GetCurrentClassLogger();
1✔
107
            logger.Debug($"Found {contentCreatorTypes.Count} content creator types to register");
1✔
108

109
            foreach (var type in contentCreatorTypes)
57✔
110
            {
27✔
111
                try
112
                {
27✔
113
                    // Register each content creator as transient
114
                    services.AddTransient(type);
27✔
115
                    logger.Debug($"Registered content creator: {type.Name}");
27✔
116
                }
27✔
117
                catch (Exception ex)
×
UNCOV
118
                {
×
UNCOV
119
                    logger.Warn($"Failed to register content creator {type.Name}: {ex.Message}");
×
UNCOV
120
                }
×
121
            }
27✔
122
        }
1✔
123

124
        internal static void RegisterAIPlugin(IServiceCollection services, IConfiguration config, IPluginLoader pluginLoader)
125
        {
3✔
126
            // Load and register AI plugin if configured
127
            if (!string.IsNullOrEmpty(config.AIPlugin))
3✔
128
            {
1✔
129
                var aiPlugin = LoadAIPlugin(config, pluginLoader);
1✔
130
                if (aiPlugin != null)
1✔
131
                {
1✔
132
                    services.AddSingleton(aiPlugin);
1✔
133
                }
1✔
134
            }
1✔
135
        }
3✔
136

137
        internal static IAISystemPlugin LoadAIPlugin(IConfiguration config, IPluginLoader pluginLoader)
138
        {
6✔
139
            // Try loading plugins from each directory
140
            var logger = NLog.LogManager.GetCurrentClassLogger();
6✔
141
            foreach (var dir in config.PluginDirs)
31✔
142
            {
9✔
143
                IAISystemPlugin plugin = null;
9✔
144
                try
145
                {
9✔
146
                    plugin = pluginLoader.LoadByName<IAISystemPlugin>(
9✔
147
                        pluginDir: dir,
9✔
148
                        typeName: config.AIPlugin,
9✔
149
                        configureGlobals: sc =>
9✔
UNCOV
150
                        {
×
UNCOV
151
                            sc.AddSingleton(config);
×
152
                        });
9✔
153
                }
8✔
154
                catch (Exception ex)
1✔
155
                {
1✔
156
                    logger.Warn($"Error loading AI plugin from directory {dir}: {ex.Message}. Will try other directories.");
1✔
157
                }
1✔
158
                try
159
                { 
9✔
160
                    if (plugin is not null)
9✔
161
                    {
5✔
162
                        logger.Info($"Found AI plugin: {plugin.Name}");
5✔
163
                        plugin.InitializePlugin(config);
5✔
164
                        return plugin;
4✔
165
                    }
166
                }
4✔
167
                catch (Exception ex)
1✔
168
                {
1✔
169
                    logger.Warn($"Error initializing AI plugin from directory {dir}: {ex.Message}");
1✔
170
                    return null;
1✔
171
                }
172
            }
4✔
173
            logger.Warn($"Could not find AI plugin '{config.AIPlugin}' in any of the plugin directories.");
1✔
174
            return null;
1✔
175
        }
6✔
176

177
        static int Main(string[] args)
178
        {
×
179
            try
180
            {
×
181
                Parser.Default.ParseArguments<CommandlineOptions>(args)
×
182
                    .WithParsed<CommandlineOptions>(options =>
×
183
                   {
×
184
                       //set up logging first
×
185
                       var assembly = Assembly.GetExecutingAssembly();
×
186
                       var projectConfigFile = $"{Path.GetDirectoryName(assembly.Location)}/RoboClerk_input/RoboClerkConfig/projectConfig.toml";
×
187
                       var roboClerkConfigFile = $"{Path.GetDirectoryName(assembly.Location)}/RoboClerk_input/RoboClerkConfig/RoboClerk.toml";
×
188
                       if (options.ConfigurationFile != null)
×
189
                       {
×
190
                           roboClerkConfigFile = options.ConfigurationFile;
×
191
                       }
×
192
                       if (options.ProjectConfigurationFile != null)
×
193
                       {
×
194
                           projectConfigFile = options.ProjectConfigurationFile;
×
195
                       }
×
196

×
197
                       try
×
198
                       {
×
199
                           ConfigureLogging(roboClerkConfigFile);
×
200
                       }
×
201
                       catch (Exception e)
×
202
                       {
×
203
                           Console.WriteLine($"An error occurred configuring Roboclerk logging: \n{e.Message}");
×
204
                           throw;
×
205
                       }
×
206
                       var logger = NLog.LogManager.GetCurrentClassLogger();
×
207
                       logger.Warn($"RoboClerk Version: {Assembly.GetExecutingAssembly().GetName().Version}");
×
208
                       var commandlineOptions = GetConfigOptions(options.ConfigurationOptions, logger);
×
209
                       try
×
210
                       {
×
211
                           var serviceCollection = new ServiceCollection();
×
212
                           serviceCollection.AddTransient<IFileProviderPlugin>(x=> new LocalFileSystemPlugin(new FileSystem()));
×
213
                           serviceCollection.AddTransient<IFileSystem, FileSystem>();
×
214
                           serviceCollection.AddSingleton<IConfiguration>(x => new RoboClerk.Configuration.Configuration(x.GetRequiredService<IFileProviderPlugin>(), roboClerkConfigFile, projectConfigFile, commandlineOptions));
×
215
                           serviceCollection.AddSingleton<IPluginLoader, PluginLoader>();
×
216
                           serviceCollection.AddSingleton<ITraceabilityAnalysis, TraceabilityAnalysis>();
×
217
                           serviceCollection.AddSingleton<IRoboClerkCore, RoboClerkTextCore>();
×
218

×
219
                           // Register all content creators
×
220
                           RegisterContentCreators(serviceCollection);
×
221

×
222
                           // Register the content creator factory with service provider injection
×
223
                           serviceCollection.AddSingleton<IContentCreatorFactory>(serviceProvider =>
×
224
                               new ContentCreatorFactory(serviceProvider, serviceProvider.GetRequiredService<ITraceabilityAnalysis>()));
×
225

×
226
                           var serviceProvider = serviceCollection.BuildServiceProvider();
×
227

×
228
                           // Register AI plugin after service provider is built
×
229
                           RegisterAIPlugin(serviceCollection, serviceProvider.GetService<IConfiguration>(), serviceProvider.GetService<IPluginLoader>());
×
230

×
231
                           //clean the output directory before we start working
×
232
                           var config = serviceProvider.GetService<IConfiguration>();
×
233
                           if (config != null && config.ClearOutputDir)
×
234
                           {
×
235
                               CleanOutputDirectory(config.OutputDir, logger);
×
236
                           }
×
237
                           if (config.CheckpointConfig.CheckpointFile == string.Empty) //check if we are not using a checkpoint
×
238
                           {
×
239
                               serviceCollection.AddSingleton<IDataSources, PluginDataSources>();
×
240
                           }
×
241
                           else
×
242
                           {
×
243
                               serviceCollection.AddSingleton<IDataSources>(x => new CheckpointDataSources(x.GetRequiredService<IConfiguration>(), x.GetRequiredService<IPluginLoader>(), x.GetRequiredService<IFileProviderPlugin>(), config.CheckpointConfig.CheckpointFile));
×
244
                           }
×
245
                           serviceProvider = serviceCollection.BuildServiceProvider();
×
246

×
247
                           var core = serviceProvider.GetService<IRoboClerkCore>();
×
248
                           core.GenerateDocs();
×
249
                           core.SaveDocumentsToDisk();
×
250
                       }
×
251
                       catch (Exception e)
×
252
                       {
×
253
                           logger.Error("An unhandled exception has occurred. RoboClerk failed to complete:\n\n");
×
254
                           logger.Error(e);
×
255
                           throw;
×
256
                       }
×
257
                   });
×
258
            }
×
UNCOV
259
            catch
×
260
            {
×
261
                return 1;
×
262
            }
UNCOV
263
            return 0;
×
UNCOV
264
        }
×
265
    }
266
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc