I have a large test set (5k+) using xUnit.net, and I'm having concurrency problems among tests running in parallel.
xUnit randomizes the execution order of tests, which makes it harder for me to detect the issue.
I'd like to know whether is there a way to log, during test execution, the moment a test starts and the moment it ends.
Note: Using constructor and disposer methods does not cut it, because you cannot know which test is being run on the constructor/disposer.
Note 2: In case it is not obvious, I'm looking for a solution that doesn't involve writing log call in each test.
Thanks,
Well, I managed to do it using the BeforeAfterTestAttribute from xUnit. Then I wrote the utility logger below to output the results to a .csv file.
public class LogTestExecutionAttribute: BeforeAfterTestAttribute
{
public override void Before(MethodInfo methodUnderTest)
{
TestExecutionDataLogger.LogBegin(methodUnderTest);
}
public override void After(MethodInfo methodUnderTest)
{
TestExecutionDataLogger.LogEnd(methodUnderTest);
}
}
public static class TestExecutionDataLogger
{
private static readonly string LogFileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "DbCoud", $"UnitTests_{DateTime.UtcNow:yyyy_MM_dd_HH_mm}_D_{AppDomain.CurrentDomain.Id}.csv");
private static int _startedOrder = 0;
private static int _endedOrder = 0;
private static readonly ConcurrentDictionary<string, testExecutionData> testDataDict = new ConcurrentDictionary<string, testExecutionData>();
private static readonly ConcurrentQueue<string> logQueue = new ConcurrentQueue<string>();
public static void LogBegin(MethodInfo testInfo)
{
var name = $"{testInfo.DeclaringType.FullName}.{testInfo.Name}";
var order = Interlocked.Add(ref _startedOrder, 1);
var startedUtc = DateTime.UtcNow;
var data = testDataDict.GetOrAdd(name, new testExecutionData());
data.StartedUtc = startedUtc;
data.StartedOrder = order;
data.TestName = name;
data.Status = "Started";
data.StartThreadId = Thread.CurrentThread.ManagedThreadId;
writeLog(data);
}
public static void LogEnd(MethodInfo testInfo)
{
var name = $"{testInfo.DeclaringType.FullName}.{testInfo.Name}";
var dataEndedUtc = DateTime.UtcNow;
var order = Interlocked.Add(ref _endedOrder, 1);
var data = testDataDict[name];
data.EndedUtc = dataEndedUtc;
data.EndedOrder = order;
data.Status = "Ended";
data.EndThreadId = Thread.CurrentThread.ManagedThreadId;
writeLog(data);
}
private static void writeLog(testExecutionData data)
{
logQueue.Enqueue(data.ToCsvLine());
if (data.EndedOrder == 1)
{
Directory.CreateDirectory(Path.GetDirectoryName(LogFileName));
Task.Run(logWriter);
}
}
private static Task logWriter()
{
while (true)
{
var logs = new List<string>();
string result;
while (logQueue.TryDequeue(out result))
{
logs.Add(result);
}
if (logs.Any())
{
File.AppendAllLines(LogFileName, logs);
}
}
}
private class testExecutionData
{
public int StartedOrder { get; set; }
public int EndedOrder { get; set; }
public DateTime StartedUtc { get; set; }
public DateTime EndedUtc { get; set; }
public string TestName { get; set; }
public string Status { get; set; }
public int StartThreadId { get; set; }
public int EndThreadId { get; set; }
public string ToCsvLine() { return $"{TestName};{Status};{StartedOrder};{EndedOrder};{StartedUtc:o};{EndedUtc:o};{Math.Max(0, ( EndedUtc - StartedUtc ).TotalMilliseconds)};{StartThreadId};{EndThreadId}"; }
}
}
To use this code, add the LogTestExecutionAttribute to the test classes you want to log (or to the base classes ;p).
Related
So I wrote a method of code and it pulls from the database correctly (I am using Dapper), but it doesnt pass off to the next method. Can anyone tell me why and what I am doing wrong? Not quite understanding what I am doing wrong here. I have tried a few different ways including below and making and IEnumerable list. I can see the variables in the logger so I know I am pulling them correctly, just not sure why they arent sending to the CheckSite().
public class UptimeService
{
private readonly ILogger<UptimeService> _logger;
private readonly IWebsiteData _webdb;
private readonly IUptimeData _db;
public UptimeService(IWebsiteData webdb, IUptimeData db ,ILogger<UptimeService> logger)
{
_webdb = webdb;
_logger = logger;
_db= db;
}
public class SiteResponse
{
public int Websiteid { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public bool Status { get; set; }
public long ResponseTime { get; set; }
}
public async Task GetAllWebsites()
{
var websites = await _webdb.GetWebsites();
foreach (var website in websites)
{
_logger.LogInformation($"WEBSITE::::: {website.Url} | {website.Name} | {website.Websiteid}");
CheckSite(website.Url, website.Name, website.Websiteid);
}
return ;
}
public SiteResponse CheckSite(string Url, string Name, int Websiteid)
{
var result = new SiteResponse();
var stopwatch = new Stopwatch();
stopwatch.Start();
var client = new HttpClient();
_logger.LogInformation(
$"TEST URL: {result.Url}");
try
{
var checkingResponse = client.GetAsync(Url).Result;
result.Status = checkingResponse.IsSuccessStatusCode &&
checkingResponse.StatusCode == HttpStatusCode.OK;
}
catch
{
result.Status = false;
// offline
}
stopwatch.Stop();
var elapsed = stopwatch.ElapsedMilliseconds;
result.ResponseTime = elapsed;
if (result.Status)
{
// archive record
RecordToDb(result);
}
else
{
_logger.LogInformation(
$"Status is {result.Status}");
}
return result;
}
public async void RecordToDb(SiteResponse response)
{
var newRecord = new UptimeModel
{
Time = DateTime.Now,
Status = response.Status,
ResponseTime = (int)response.ResponseTime,
Websiteid = response.Websiteid,
Name = response.Name,
};
_logger.LogInformation(
$"Trying to Save {response.Name}");
await _db.InsertUptime(newRecord);
}
}
If the result.Url is empty here:
_logger.LogInformation($"TEST URL: {result.Url}");
that's because it's a new instance of SiteResponse() method.
If it is showing as null, you'll need to create constructors on the class. Here is an example:
public class SiteResponse
{
public SiteResponse(){ }
public SiteResponse(string url){
Url = url;
}
public int Websiteid { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public bool Status { get; set; }
public long ResponseTime { get; set; }
}
and then initialize the new one like:
var result = new SiteResponse(Url);
Based on the comments below, I would refactor to something like this.
public class UptimeService
{
private readonly ILogger<UptimeService> _logger;
private readonly IWebsiteData _webdb;
private readonly IUptimeData _db;
public UptimeService(IWebsiteData webdb, IUptimeData db ,ILogger<UptimeService> logger)
{
_webdb = webdb;
_logger = logger;
_db= db;
}
public class SiteResponse
{
public int Websiteid { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public bool Status { get; set; }
public long ResponseTime { get; set; }
}
public async Task GetAllWebsites()
{
var websites = await _webdb.GetWebsites();
foreach (var website in websites)
{
_logger.LogInformation($"WEBSITE::::: {website.Url} | {website.Name} | {website.Websiteid}");
await CheckSite(website);
}
return ;
}
public async Task CheckSite(SiteResponse siteResponse)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
var client = new HttpClient();
_logger.LogInformation(
$"TEST URL: {siteResponse.Url}");
try
{
var checkingResponse = await client.GetAsync(siteResponse.Url);
siteResponse.Status = checkingResponse.IsSuccessStatusCode &&
checkingResponse.StatusCode == HttpStatusCode.OK;
}
catch
{
siteResponse.Status = false;
// offline
}
stopwatch.Stop();
var elapsed = stopwatch.ElapsedMilliseconds;
siteResponse.ResponseTime = elapsed;
if (siteResponse.Status)
{
// archive record
RecordToDb(siteResponse);
}
else
{
_logger.LogInformation(
$"Status is {siteResponse.Status}");
}
return;
}
public async void RecordToDb(SiteResponse response)
{
var newRecord = new UptimeModel
{
Time = DateTime.Now,
Status = response.Status,
ResponseTime = (int)response.ResponseTime,
Websiteid = response.Websiteid,
Name = response.Name,
};
_logger.LogInformation(
$"Trying to Save {response.Name}");
await _db.InsertUptime(newRecord);
}
}
In my program I have a set of Links, let's say a million. The requirements are:
1) Process the links in parallel
2) Limit how many of these links will run in parallel.
In the example, we don't care about the execution order of the links but to ensure the process is finished with errors or without.
How can I properly ensure the above conditions?
class Program
{
private static void Main()
{
start_url_list();
}
static void start_url_list()
{
HashSet<string> urls = get_url_list();
Parallel.ForEach(urls, new ParallelOptions { MaxDegreeOfParallelism = 10 },
url =>
{
Console.WriteLine("Trying..:{0}", url);
var my_url = new Url_Class(url);
my_url.Start();
Console.WriteLine("Processing url is over");
//Do stuff with my_url properties
if (my_url.isError)
{
Console.WriteLine("ERROR:{0} msg:{1}",url,my_url.errorMsg);
}
else
{
Console.WriteLine("OK:{0} Size{1}", url, my_url.size);
}
});
}
}
public class Url_Class
{
public string url { get; private set; }
public string errorMsg { get; internal set; }
public bool isError { get; private set; }
public int size { get; private set; }
public Host(string url_str)
{
url = url_str;
isError = false;
}
public void Start()
{
//Long Process
}
}
In ASP.NET website I have static readonly XGeoPhone provider = new XGeoPhone();. Inside of XGeoPhone class I have static readonly XPhoneInfo[] phoneInfo = new[] { new XPhoneInfo(......), ... 250000 more objects ... }. When I run this on developer machine or real server — it crashes at new XGeoPhone() with StackOverflowException. I do not quite understand it tries to create this giant array on stack, not heap? What is going on?
UPDATE: Simplest version that will crash:
public partial class XGeoPhone
{
public XPhoneInfo GetInfo(long phone)
{
return
phoneInfos.
FirstOrDefault(pi =>
pi.Start <= phone &&
phone <= pi.End);
}
static readonly XPhoneInfo[] phoneInfos = new[]
{
new XPhoneInfo(2000000000, 2099999999, XCountryId.EG, null, null),
new XPhoneInfo(2120000000, 2129999999, XCountryId.MA, null, null),
new XPhoneInfo(2130000000, 2139999999, XCountryId.DZ, null, null),
new XPhoneInfo(2160000000, 2169999999, XCountryId.TN, null, null),
new XPhoneInfo(2180000000, 2189999999, XCountryId.LY, null, null),
.........
}
}
public class XPhoneInfo
{
public XPhoneInfo(long start, long end, XCountryId? country, string region, string provider)
{
this.Start = start;
this.End = end;
this.Country = country;
this.Region = region;
this.Provider = provider;
}
public long Start { get; private set; }
public long End { get; private set; }
public XCountryId? Country { get; private set; }
public string Region { get; private set; }
public string Provider { get; private set; }
}
class Program
{
static readonly XGeoPhone g = new XGeoPhone();
static void Main(string[] args)
{
var v = g.GetInfo(79054567890);
}
}
Okay, I found the workaround — I can create a thread and specify the stack size in a thread’s constructor, like described here. It works. But I decided to move that big array to csv file.
i am working on a small app that mange flights, i have a class that build a flight details and class that build the passenger, now, i want to load the passengers onto a flight, how should i do it? do i need to build a higer class that inherit from this two class and make a list of that type of class(i dont think that wise oop ).or should i add a ticket prop in the passenger class that have the flight number, here is my code.
public class Passenger
{
public Passenger(string name, int passportNumber)
{
this.PassengerName = name;
this.PassportNumber = passportNumber;
}
private string _passengerName;
public string PassengerName
{
get { return _passengerName; }
set { _passengerName = value; }
}
private int _passportNumber;
public int PassportNumber
{
get { return _passportNumber; }
set { _passportNumber = value; }
}
}
public class FlightDetails
{
public FlightDetails(int flightNumber, string flightDestination, string planmodel)
{
this.FlightNumber = flightNumber;
this.FlightDestination = flightDestination;
this.PlanModel = planmodel;
}
private int _flightNumber;
public int FlightNumber
{
get { return _flightNumber; }
set { _flightNumber = value; }
}
private string _flightDestination;
public string FlightDestination
{
get { return _flightDestination; }
set { _flightDestination = value; }
}
private string _planeModel;
public string PlanModel
{
get { return _planeModel; }
set { _planeModel = value; }
}
}
static void Main(string[] args)
{
List<FlightDetails> flightList = new List<FlightDetails>();
FlightDetails a = new FlightDetails(12,"france","jumbo");///create a flight
flightList.Add(a);/// load up the flight
}
First, you can't create a class that inherits from both other classes because multiply inheritance is not allowed in C#.
You can use aggregation, something like this:
public class FlightDetails
{
// ...
}
public class Passenger
{
// ...
}
public class Flight
{
public FlightDetails { get; private set; }
public List<Passenger> Passengers { get; private set; }
public Flight(FlightDetails details)
{
FlightDetails = details;
Passengers = new List<Passenger>();
}
public AddPassenger(Passenger p)
{
// check for ticket and so on..
Passengers.Add(p);
}
}
You can read more about aggregation here: http://en.wikipedia.org/wiki/Object_composition#Aggregation
Note that in this example for simplicity i used List but actually you need to limit access to this array (because otherwise i can do something like this: Flight.Passengers.Add(p) instead of Flight.AddPassenger(p)) so good idea will be use ReadOnlyCollection as public interface to this list.
Here's a sample code that might work. A flight has one or more passengers, thus has a List of type Passenger. In real-life, a passenger can book multiple flights. If you want the reality, you'll have to change your model but for this situation it'll work:
public class Passenger
{
public Passenger(string name, int passportNumber)
{
PassengerName = name;
PassportNumber = passportNumber
}
public string PassengerName { get; set; }
public int PassportNumber { get; set; }
}
public class FlightDetails
{
public FlightDetails(int flightNumber, string flightDestination, string planmodel)
{
FlightNumber = flightNumber;
FlightDestination = flightDestination;
PlanModel = planmodel;
Passengers = new List<Passengers>();
}
public int FlightNumber { get; set; }
public string FlightDestination { get; set; }
public string PlanModel { get; set; }
public List<Passenger> Passengers { get; private set; }
public void AddPassenger(string name, int number)
{
int max = 2;
int passengersNumber = Passengers.Count;
if (passengersNumber < max)
{
Passengers.Add(new Passenger(name, number);
}
}
public static void Main(string[] args)
{
var flightList = new List<FlightDetails>();
var passengersList = new List<Passenger>();
//Add passenger-objects to passengers-list
var flightOne = new FlightDetails(12, "France", "Jumbo");
flightOne.Passengers = passengersList;
flightList.Add(a);
}
Here's a better solution to limit the passengers:
public class FlightDetails
{
public FlightDetails(int flightNumber, string flightDestination, string planmodel)
: this(flightNumber, flightDestination, planmodel, new List<Passenger>())
{
}
public FlightDetails(int flightNumber, string flightDestination, string planmodel, List<Passenger> passengers)
{
FlightNumber = flightNumber;
FlightDestination = flightDestination;
PlanModel = planmodel;
if(passengers.Count > 2)
//throw exception or error
else
_passengers = passengers;
}
private List<Passenger> _passengers = new List<Passenger>();
public int FlightNumber { get; set; }
public string FlightDestination { get; set; }
public string PlanModel { get; set; }
public IEnumerable<Passenger> Passengers { get { return _passengers; } }
public void AddPassenger(string name, int number)
{
int max = 2;
int passengersNumber = _passengers.Count;
if (passengersNumber < max)
{
_passengers.Add(new Passenger(name, number);
}
}
}
Note: this code is written without compiling. But the idea is correct normally. :)
In logical way, relation between FlightDetail to Passenger is OneToMany. One FlightDetail can have multiple Passenger which is can be written as below. FlightDetail and Passenger should be have any common inheritance hierarchy because they are don't have any common attribute or behaviour.
public class FlightDetails
{
private List<Passenger> passengerList;
public void addPassenger(Passenger p){
if(passengerList == null){
passengerList = new ArrayList<Passenger>();
}
passengerList.add(p);
}
public List<Passenger> getPassengerList(){
return passengerList;
}
//... your other detail
}
You should add a FlightDetails property to your Passenger class. That's easier than making a List with PassportNumber as index. But, it's easier to iterate FlightDetails using List, than accessing it through Passenger.
It actually depends on how you want to access and store the relations.
It might be a good idea to read about the composite pattern which actually has a nice solution for travelling between parent-child relations, even though the pattern has another purpose.
Here is code:
public interface IAccessPoint
{
int BackHaulMaximum { get; set; }
bool BackHaulMaximumReached();
void EmailNetworkProvider();
}
public class AccessPoint : IAccessPoint
{
private IMailProvider Mailer { get; set; }
public AccessPoint(IMailProvider provider)
{
this.Mailer = provider ?? new DefaultMailProvider();
}
public int BackHaulMaximum { get; set; }
public bool BackHaulMaximumReached()
{
if (BackHaulMaximum > 80)
{
EmailNetworkProvider();
return true;
}
return false;
}
public void EmailNetworkProvider()
{
this.Mailer.SendMail();
}
}
public interface IMailProvider
{
void SendMail();
}
public class DefaultMailProvider : IMailProvider
{
public void SendMail()
{
}
}
// Here is the Test, It is not calling EmailNetworkProvider which calls SendMail()
[TestFixture]
public class Tests
{
[Test]
public void NetworkProviderShouldBeEmailedWhenBackHaulMaximumIsReached()
{
var mailerMock = MockRepository.GenerateMock<IMailProvider>();
mailerMock.Expect(x => x.SendMail());
var accessPoint = new AccessPoint(mailerMock);
accessPoint.BackHaulMaximum = 81;
Assert.IsTrue(accessPoint.BackHaulMaximumReached());
mailerMock.VerifyAllExpectations();
}
}
Any improvement if you use this test?
[Test]
public void NetworkProviderShouldBeEmailedWhenBackHaulMaximumIsReached()
{
var mailerMock = MockRepository.GenerateStub<IMailProvider>();
var accessPoint = new AccessPoint(mailerMock);
accessPoint.BackHaulMaximum = 81;
var actual = accessPoint.BackHaulMaximumReached();
Assert.AreEqual(true, actual);
mailerMock.AssertWasCalled(x => x.SendMail());
}
As a side-note, BackhaulMaximumReached() is kind of a bizarre design. No notification will be made unless a consumer checks whether the back haul maximum was reached, regardless of the value of BackHaulMaximum.
It is semantically confusing to comingle commands and queries in this way.