Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ImageComparerService.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.ComponentModel;
6 using System.Configuration.Install;
7 using System.IO;
8 using System.Linq;
9 using System.Net;
10 using System.Net.Sockets;
11 using System.Reflection;
12 using System.Runtime.InteropServices;
13 using System.ServiceProcess;
14 using System.Threading;
15 using Mono.Options;
16 using SiliconStudio.Paradox.Graphics;
17 using SiliconStudio.Paradox.Graphics.Regression;
18 
19 namespace SiliconStudio.ImageComparerService
20 {
22  {
23  public const string DefaultServiceName = "SiliconStudio Image Comparer Service";
24 
25  private int port = 1832;
26  private string baseOutputPath;
27  private Dictionary<TcpClient, Thread> clients = new Dictionary<TcpClient, Thread>();
28  private Thread listenerThread;
29  private TcpListener server;
30 
32  {
33  ServiceName = DefaultServiceName;
34  }
35 
36  /// <summary>
37  /// Perform a comparison between the generated image and the base one.
38  /// </summary>
39  /// <param name="receivedImage">The received image.</param>
40  /// <returns>True if the tests were correctly performed.</returns>
41  private bool CompareImage(TestResultServerImage receivedImage, string resultTempFileName)
42  {
43  var comparer = new ImageComparator { SaveJson = true };
44 
45  return comparer.Compare_RMSE(receivedImage, resultTempFileName);
46  }
47 
48  private void ProcessSendImage(ImageComparerClient imageComparerClient, BinaryReader reader, BinaryWriter binaryWriter)
49  {
50  var receivedImage = new TestResultServerImage(imageComparerClient);
51 
52  // Read image header
53  receivedImage.ClientImage.Read(reader);
54 
55  Console.WriteLine(@"Receiving {0} from {1}", receivedImage.ClientImage.TestName, imageComparerClient.Connection.Serial);
56 
57  // Compute paths
58  var goldPath = Path.Combine(baseOutputPath, receivedImage.GetGoldDirectory());
59  var outputPath = Path.Combine(baseOutputPath, receivedImage.GetOutputDirectory());
60 
61  receivedImage.GoldPath = goldPath;
62  receivedImage.OutputPath = outputPath;
63  receivedImage.JsonPath = Path.Combine(baseOutputPath, "json");
64 
65  Directory.CreateDirectory(receivedImage.GoldPath);
66  Directory.CreateDirectory(receivedImage.OutputPath);
67  Directory.CreateDirectory(receivedImage.JsonPath);
68 
69  receivedImage.GoldFileName = Path.Combine(goldPath, receivedImage.GetFileName());
70  receivedImage.ResultFileName = Path.Combine(outputPath, receivedImage.GetFileName());
71  receivedImage.DiffFileName = Path.Combine(outputPath, receivedImage.GetDiffFileName());
72  receivedImage.DiffNormFileName = Path.Combine(outputPath, receivedImage.GetNormDiffFileName());
73 
74  // Seems like image magick doesn't like to read from UNC path (size is OK but not data?)
75  // Let's use temp path to do most of the work before copying
76  var resultTempFileName = Path.GetTempPath() + Guid.NewGuid() + ".png";
77  try
78  {
79  // Read image data
80  using (var image = receivedImage.ClientImage.Image)
81  using (var resultFileStream = File.OpenWrite(resultTempFileName))
82  {
83  image.Save(resultFileStream, ImageFileType.Png);
84  }
85 
86  CompareImage(receivedImage, resultTempFileName);
87  }
88  finally
89  {
90  File.Delete(resultTempFileName);
91  }
92 
93  var receivedImages = imageComparerClient.Images;
94  List<TestResultServerImage> receivedImageForThisTest;
95  lock (receivedImages)
96  {
97  if (!receivedImages.TryGetValue(receivedImage.ClientImage.TestName, out receivedImageForThisTest))
98  {
99  receivedImageForThisTest = new List<TestResultServerImage>();
100  receivedImage.FrameIndex = receivedImageForThisTest.Count;
101  receivedImages.Add(receivedImage.ClientImage.TestName, receivedImageForThisTest);
102  }
103  }
104 
105  receivedImageForThisTest.Add(receivedImage);
106 
107  // Send ack
108  binaryWriter.Write(true);
109  }
110 
111  /// <summary>
112  /// Process a request for image comparison (client uses it to know if test succeeded or failed).
113  /// </summary>
114  /// <param name="binaryReader">The binary reader.</param>
115  /// <param name="binaryWriter">The binary writer.</param>
116  private void ProcessRequestImageComparisonStatus(ImageComparerClient imageComparerClient, BinaryReader binaryReader, BinaryWriter binaryWriter)
117  {
118  var testName = binaryReader.ReadString();
119 
120  var receivedImages = imageComparerClient.Images;
121  List<TestResultServerImage> receivedImageForThisTest;
122  lock (receivedImages)
123  {
124  if (!receivedImages.TryGetValue(testName, out receivedImageForThisTest))
125  {
126  // No image, return false?!
127  binaryWriter.Write(true);
128  return;
129  }
130  }
131 
132  // perform comparisons
133  var testSucceeded = true;
134  foreach (var receivedImage in receivedImageForThisTest)
135  {
136  testSucceeded &= receivedImage.MeanSquareError == 0.0f;
137  }
138 
139  binaryWriter.Write(testSucceeded);
140  binaryWriter.Flush();
141  }
142 
143  private void ClientThread(object res)
144  {
145  var client = (TcpClient)res;
146 
147  var imageComparerClient = new ImageComparerClient();
148 
149  lock (clients)
150  {
151  clients[client] = Thread.CurrentThread;
152  }
153 
154  try
155  {
156  using (var networkStream = client.GetStream())
157  {
158  var binaryReader = new BinaryReader(networkStream);
159  var binaryWriter = new BinaryWriter(networkStream);
160 
161  // Read common information to all tests (device, branch, etc...)
162  imageComparerClient.Connection.Read(binaryReader);
163 
164  // Process requests
165  while (true)
166  {
167  var messageType = (ImageServerMessageType)binaryReader.ReadInt32();
168  if (messageType == ImageServerMessageType.ConnectionFinished)
169  break;
170 
171  switch (messageType)
172  {
173  case ImageServerMessageType.SendImage:
174  // Receives an image
175  ProcessSendImage(imageComparerClient, binaryReader, binaryWriter);
176  break;
177  case ImageServerMessageType.RequestImageComparisonStatus:
178  // Returns comparison status
179  ProcessRequestImageComparisonStatus(imageComparerClient, binaryReader, binaryWriter);
180  break;
181  }
182  }
183  }
184 
185  client.Close();
186  }
187  catch (Exception e)
188  {
189  Console.WriteLine("Client thread exception: {0}", e);
190  }
191 
192  lock (clients)
193  {
194  clients.Remove(client);
195  }
196  }
197 
198  public void ListenBase()
199  {
200  server = new TcpListener(IPAddress.Any, port);
201  server.Start();
202 
203  try
204  {
205  while (true)
206  {
207  var client = server.AcceptTcpClient();
208 
209  Console.WriteLine(@"Accepted a new connection from client '{0}'.", client.Client.RemoteEndPoint);
210 
211  new Thread(ClientThread).Start(client);
212  }
213  }
214  catch (SocketException)
215  {
216  // Probably OnStop closing socket
217  }
218  }
219 
220  protected override void OnStart(string[] args)
221  {
222  base.OnStart(args);
223 
224  var exeName = Path.GetFileName(Assembly.GetExecutingAssembly().Location);
225  var showHelp = false;
226  baseOutputPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SiliconStudio", "ImageComparerResults");
227 
228  var p = new OptionSet
229  {
230  "Copyright (C) 2011-2013 Silicon Studio Corporation. All Rights Reserved",
231  "Paradox Test Suite Tool - Version: "
232  +
233  String.Format(
234  "{0}.{1}.{2}",
235  typeof(ImageComparerService).Assembly.GetName().Version.Major,
236  typeof(ImageComparerService).Assembly.GetName().Version.Minor,
237  typeof(ImageComparerService).Assembly.GetName().Version.Build) + string.Empty,
238  string.Format("Usage: {0} [--port=port] [--output=folder", exeName),
239  string.Empty,
240  "=== Options ===",
241  string.Empty,
242  { "h|help", @"Show this message and exit", v => showHelp = v != null },
243  { "p|port=", @"Port (default: 1832)", v => port = int.Parse(v) },
244  { "o|output=", @"Output folder (default: %APPDATA%\SiliconStudio\ImageComparerResults)", v => baseOutputPath = v },
245  };
246 
247  try
248  {
249  var commandArgs = p.Parse(args);
250  if (commandArgs.Count > 0)
251  throw new OptionException();
252 
253  if (showHelp)
254  {
255  p.WriteOptionDescriptions(Console.Out);
256  Stop();
257  return;
258  }
259 
260  Directory.CreateDirectory(baseOutputPath);
261 
262  listenerThread = new Thread(ListenBase);
263  listenerThread.Start();
264  }
265  catch (Exception e)
266  {
267  Console.WriteLine("{0}: {1}", exeName, e);
268  if (e is OptionException)
269  p.WriteOptionDescriptions(Console.Out);
270  Stop();
271  }
272  }
273 
274  protected override void OnStop()
275  {
276  // Stop main socket & thread
277  if (server != null)
278  {
279  server.Stop();
280  server = null;
281  }
282 
283  if (listenerThread != null)
284  {
285  listenerThread.Join();
286  listenerThread = null;
287  }
288 
289  // Stop client threads
290  KeyValuePair<TcpClient, Thread>[] currentClients;
291  lock (clients)
292  {
293  currentClients = clients.ToArray();
294  clients.Clear();
295  }
296 
297  foreach (var client in currentClients)
298  {
299  client.Key.Close();
300  client.Value.Join();
301  }
302  }
303 
304  /// <summary>
305  /// The Main Thread: This is where your Service is Run.
306  /// </summary>
307  static void Main(string[] args)
308  {
309  if (args.Length > 0 && args[0] == "--console")
310  {
311  var imageComparerService = new ImageComparerService();
312  imageComparerService.OnStart(args.Skip(1).ToArray());
313  imageComparerService.listenerThread.Join();
314  imageComparerService.OnStop();
315  }
316  else if (args.Length > 0 && args[0] == "--install")
317  {
318  ManagedInstallerClass.InstallHelper(new[] { Assembly.GetExecutingAssembly().Location });
319  }
320  else if (args.Length > 0 && args[0] == "--uninstall")
321  {
322  ManagedInstallerClass.InstallHelper(new[] { "/u", Assembly.GetExecutingAssembly().Location });
323  }
324  else
325  {
326  ServiceBase.Run(new ImageComparerService());
327  }
328  }
329  }
330 
331  [RunInstaller(true)]
333  {
335  {
336  var processInstaller = new ServiceProcessInstaller();
337  var serviceInstaller = new ServiceInstaller();
338 
339  //set the privileges
340  processInstaller.Account = ServiceAccount.LocalSystem;
341 
342  serviceInstaller.DisplayName = ImageComparerService.DefaultServiceName;
343  serviceInstaller.StartType = ServiceStartMode.Automatic;
344 
345  //must be the same as what was set in Program's constructor
346  serviceInstaller.ServiceName = ImageComparerService.DefaultServiceName;
347  this.Installers.Add(processInstaller);
348  this.Installers.Add(serviceInstaller);
349  }
350  }
351 }
System.IO.File File