Skip to content

Commit 8c2be45

Browse files
committed
* Parallelize tests
* Split TestBasicRequestHandling into multiple tests so it can be parallelized * Use different port numbers to support parallelization * General code clean-up
1 parent 90afb77 commit 8c2be45

9 files changed

Lines changed: 526 additions & 448 deletions

.editorconfig

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ indent_size = 2
2929
# Organize usings
3030
dotnet_separate_import_directive_groups = false
3131
dotnet_sort_system_directives_first = true
32-
file_header_template =
32+
file_header_template =
3333

3434
# this. and Me. preferences
3535
dotnet_style_qualification_for_event = false
@@ -219,48 +219,48 @@ dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case
219219

220220
dotnet_naming_symbols.interface.applicable_kinds = interface
221221
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
222-
dotnet_naming_symbols.interface.required_modifiers =
222+
dotnet_naming_symbols.interface.required_modifiers =
223223

224224
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
225225
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
226-
dotnet_naming_symbols.types.required_modifiers =
226+
dotnet_naming_symbols.types.required_modifiers =
227227

228228
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
229229
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
230-
dotnet_naming_symbols.non_field_members.required_modifiers =
230+
dotnet_naming_symbols.non_field_members.required_modifiers =
231231

232232
dotnet_naming_symbols.private_method.applicable_kinds = method
233233
dotnet_naming_symbols.private_method.applicable_accessibilities = private
234-
dotnet_naming_symbols.private_method.required_modifiers =
234+
dotnet_naming_symbols.private_method.required_modifiers =
235235

236236
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
237237
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private
238-
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
238+
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
239239

240240
dotnet_naming_symbols.constant_fields.applicable_kinds = field
241241
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
242242
dotnet_naming_symbols.constant_fields.required_modifiers = const
243243

244244
# Naming styles
245245

246-
dotnet_naming_style.pascal_case.required_prefix =
247-
dotnet_naming_style.pascal_case.required_suffix =
248-
dotnet_naming_style.pascal_case.word_separator =
246+
dotnet_naming_style.pascal_case.required_prefix =
247+
dotnet_naming_style.pascal_case.required_suffix =
248+
dotnet_naming_style.pascal_case.word_separator =
249249
dotnet_naming_style.pascal_case.capitalization = pascal_case
250250

251251
dotnet_naming_style.begins_with_i.required_prefix = I
252-
dotnet_naming_style.begins_with_i.required_suffix =
253-
dotnet_naming_style.begins_with_i.word_separator =
252+
dotnet_naming_style.begins_with_i.required_suffix =
253+
dotnet_naming_style.begins_with_i.word_separator =
254254
dotnet_naming_style.begins_with_i.capitalization = pascal_case
255255

256-
dotnet_naming_style.camel_case.required_prefix =
257-
dotnet_naming_style.camel_case.required_suffix =
258-
dotnet_naming_style.camel_case.word_separator =
256+
dotnet_naming_style.camel_case.required_prefix =
257+
dotnet_naming_style.camel_case.required_suffix =
258+
dotnet_naming_style.camel_case.word_separator =
259259
dotnet_naming_style.camel_case.capitalization = camel_case
260260

261261
dotnet_naming_style.begins_with__.required_prefix = _
262-
dotnet_naming_style.begins_with__.required_suffix =
263-
dotnet_naming_style.begins_with__.word_separator =
262+
dotnet_naming_style.begins_with__.required_suffix =
263+
dotnet_naming_style.begins_with__.word_separator =
264264
dotnet_naming_style.begins_with__.capitalization = camel_case
265265

266266
#### Warning and error overrides ####
@@ -316,3 +316,12 @@ resharper_member_can_be_private_local_highlighting = none
316316

317317
# xUnit: don't assert equals on length = 1 - this tip is terrible for code consistency
318318
dotnet_diagnostic.xUnit2013.severity = none
319+
320+
# IDE0057: Use range operator
321+
dotnet_diagnostic.IDE0057.severity = none
322+
323+
# SYSLIB1045: Convert to 'GeneratedRegexAttribute'.
324+
dotnet_diagnostic.SYSLIB1045.severity = none
325+
326+
# CA1859: Use concrete types when possible for improved performance
327+
dotnet_diagnostic.CA1859.severity = none

Tests/AjaxHandlerTests.cs

Lines changed: 76 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -5,98 +5,97 @@
55
using RT.Json;
66
using RT.Util.ExtensionMethods;
77

8-
namespace RT.Servers.Tests
8+
namespace RT.Servers.Tests;
9+
10+
[TestClass]
11+
public sealed class AjaxHandlerTests
912
{
10-
[TestClass]
11-
public sealed class AjaxHandlerTests
12-
{
13-
sealed class TempObject { public int Value; }
13+
sealed class TempObject { public int Value; }
1414

15-
sealed class Ajax
16-
{
17-
[AjaxMethod]
18-
public string HelloWorld() => "Hello, world!";
15+
sealed class Ajax
16+
{
17+
[AjaxMethod]
18+
public string HelloWorld() => "Hello, world!";
1919

20-
[AjaxMethod]
21-
public string AddOne(int i) => $"{i + 1}";
20+
[AjaxMethod]
21+
public string AddOne(int i) => $"{i + 1}";
2222

23-
[AjaxMethod]
24-
public string ReturnUrl([AjaxRequest] HttpRequest req) => req.Url.ToFull();
23+
[AjaxMethod]
24+
public string ReturnUrl([AjaxRequest] HttpRequest req) => req.Url.ToFull();
2525

26-
[AjaxMethod]
27-
public int AddOneToTempObject(TempObject tempObj) => tempObj.Value + 1;
26+
[AjaxMethod]
27+
public int AddOneToTempObject(TempObject tempObj) => tempObj.Value + 1;
2828

29-
[AjaxConverter]
30-
public TempObject ConvertTempObject(int tempObj) => new TempObject { Value = tempObj };
31-
}
29+
[AjaxConverter]
30+
public TempObject ConvertTempObject(int tempObj) => new() { Value = tempObj };
31+
}
3232

33-
[TestMethod, Timeout(60 * 1000, CooperativeCancellation = true)]
34-
public void TestErrorHandlerExceptions()
33+
[TestMethod, Timeout(60 * 1000, CooperativeCancellation = true)]
34+
public void TestErrorHandlerExceptions()
35+
{
36+
var instance = new HttpServer(TestHelpers.Port + 0, new HttpServerOptions { OutputExceptionInformation = true });
37+
try
3538
{
36-
var instance = new HttpServer(TestHelpers.Port, new HttpServerOptions { OutputExceptionInformation = true });
37-
try
38-
{
39-
var ajaxHandler = new AjaxHandler<Ajax>();
40-
var api = new Ajax();
41-
instance.Handler = req => ajaxHandler.Handle(req, api);
42-
instance.StartListening();
39+
var ajaxHandler = new AjaxHandler<Ajax>();
40+
var api = new Ajax();
41+
instance.Handler = req => ajaxHandler.Handle(req, api);
42+
instance.StartListening();
4343

44-
(HttpStatusCode status, string response) getResponse(string method, string payload)
45-
{
46-
TcpClient cl = new TcpClient();
47-
cl.Connect("localhost", TestHelpers.Port);
48-
cl.ReceiveTimeout = 1000; // 1 sec
49-
var payloadFull = "data=" + payload.ToUtf8().Select(b => "%" + b.ToString("X2")).JoinString();
50-
cl.Client.Send($"POST /{method} HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: {payloadFull.Utf8Length()}\r\n\r\n{payloadFull}".ToUtf8());
51-
var response = Encoding.UTF8.GetString(cl.Client.ReceiveAllBytes());
52-
cl.Close();
53-
var code = (HttpStatusCode) int.Parse(response.Substring("HTTP/1.1 ".Length, 3));
54-
var parts = response.Split("\r\n\r\n");
55-
return (code, parts[1]);
56-
}
44+
static (HttpStatusCode status, string response) getResponse(string method, string payload)
45+
{
46+
TcpClient cl = new();
47+
cl.Connect("localhost", TestHelpers.Port + 0);
48+
cl.ReceiveTimeout = 1000; // 1 sec
49+
var payloadFull = "data=" + payload.ToUtf8().Select(b => "%" + b.ToString("X2")).JoinString();
50+
cl.Client.Send($"POST /{method} HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: {payloadFull.Utf8Length()}\r\n\r\n{payloadFull}".ToUtf8());
51+
var response = Encoding.UTF8.GetString(cl.Client.ReceiveAllBytes());
52+
cl.Close();
53+
var code = (HttpStatusCode) int.Parse(response.Substring("HTTP/1.1 ".Length, 3));
54+
var parts = response.Split("\r\n\r\n");
55+
return (code, parts[1]);
56+
}
5757

58-
// Test that we get a 404 response for an invalid method
59-
var resp = getResponse("InvalidMethod", new JsonDict { }.ToString());
60-
Assert.AreEqual(HttpStatusCode._404_NotFound, resp.status);
58+
// Test that we get a 404 response for an invalid method
59+
var resp = getResponse("InvalidMethod", new JsonDict { }.ToString());
60+
Assert.AreEqual(HttpStatusCode._404_NotFound, resp.status);
6161

62-
// Test that we get a 200 response for a valid method
63-
resp = getResponse("HelloWorld", new JsonDict { }.ToString());
64-
Assert.AreEqual(HttpStatusCode._200_OK, resp.status);
65-
Assert.IsTrue(JsonDict.TryParse(resp.response, out var json));
66-
Assert.AreEqual("Hello, world!", json["result"].GetString());
67-
Assert.AreEqual("ok", json["status"].GetString());
62+
// Test that we get a 200 response for a valid method
63+
resp = getResponse("HelloWorld", new JsonDict { }.ToString());
64+
Assert.AreEqual(HttpStatusCode._200_OK, resp.status);
65+
Assert.IsTrue(JsonDict.TryParse(resp.response, out var json));
66+
Assert.AreEqual("Hello, world!", json["result"].GetString());
67+
Assert.AreEqual("ok", json["status"].GetString());
6868

69-
// Test that we get a 400 response for an invalid request (missing parameter)
70-
resp = getResponse("AddOne", new JsonDict { }.ToString());
71-
Assert.AreEqual(HttpStatusCode._400_BadRequest, resp.status);
72-
Assert.IsTrue(JsonDict.TryParse(resp.response, out json));
73-
Assert.AreEqual("error", json["status"].GetString());
69+
// Test that we get a 400 response for an invalid request (missing parameter)
70+
resp = getResponse("AddOne", new JsonDict { }.ToString());
71+
Assert.AreEqual(HttpStatusCode._400_BadRequest, resp.status);
72+
Assert.IsTrue(JsonDict.TryParse(resp.response, out json));
73+
Assert.AreEqual("error", json["status"].GetString());
7474

75-
// Test that we get the correct response for the AddOne method
76-
resp = getResponse("AddOne", new JsonDict { ["i"] = 47 }.ToString());
77-
Assert.AreEqual(HttpStatusCode._200_OK, resp.status);
78-
Assert.IsTrue(JsonDict.TryParse(resp.response, out json));
79-
Assert.AreEqual("48", json["result"].GetString());
80-
Assert.AreEqual("ok", json["status"].GetString());
75+
// Test that we get the correct response for the AddOne method
76+
resp = getResponse("AddOne", new JsonDict { ["i"] = 47 }.ToString());
77+
Assert.AreEqual(HttpStatusCode._200_OK, resp.status);
78+
Assert.IsTrue(JsonDict.TryParse(resp.response, out json));
79+
Assert.AreEqual("48", json["result"].GetString());
80+
Assert.AreEqual("ok", json["status"].GetString());
8181

82-
// Test that we get the correct response for the ReturnUrl method
83-
resp = getResponse("ReturnUrl", new JsonDict { }.ToString());
84-
Assert.AreEqual(HttpStatusCode._200_OK, resp.status);
85-
Assert.IsTrue(JsonDict.TryParse(resp.response, out json));
86-
Assert.AreEqual("http://localhost/ReturnUrl", json["result"].GetString());
87-
Assert.AreEqual("ok", json["status"].GetString());
82+
// Test that we get the correct response for the ReturnUrl method
83+
resp = getResponse("ReturnUrl", new JsonDict { }.ToString());
84+
Assert.AreEqual(HttpStatusCode._200_OK, resp.status);
85+
Assert.IsTrue(JsonDict.TryParse(resp.response, out json));
86+
Assert.AreEqual("http://localhost/ReturnUrl", json["result"].GetString());
87+
Assert.AreEqual("ok", json["status"].GetString());
8888

89-
// Test AjaxConverter
90-
resp = getResponse("AddOneToTempObject", new JsonDict { ["tempObj"] = 47 }.ToString());
91-
Assert.AreEqual(HttpStatusCode._200_OK, resp.status);
92-
Assert.IsTrue(JsonDict.TryParse(resp.response, out json));
93-
Assert.AreEqual(48, json["result"].GetInt());
94-
Assert.AreEqual("ok", json["status"].GetString());
95-
}
96-
finally
97-
{
98-
instance.StopListening(brutal: true);
99-
}
89+
// Test AjaxConverter
90+
resp = getResponse("AddOneToTempObject", new JsonDict { ["tempObj"] = 47 }.ToString());
91+
Assert.AreEqual(HttpStatusCode._200_OK, resp.status);
92+
Assert.IsTrue(JsonDict.TryParse(resp.response, out json));
93+
Assert.AreEqual(48, json["result"].GetInt());
94+
Assert.AreEqual("ok", json["status"].GetString());
95+
}
96+
finally
97+
{
98+
instance.StopListening(brutal: true);
10099
}
101100
}
102101
}

Tests/DisconnectionTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ private IEnumerable<string> enumInfinite()
2424
[TestMethod, Timeout(60 * 1000, CooperativeCancellation = true)]
2525
public void TestMidResponseSocketClosure()
2626
{
27-
var instance = new HttpServer(TestHelpers.Port)
27+
var instance = new HttpServer(TestHelpers.Port + 1)
2828
{
2929
Handler = new UrlResolver(
3030
new UrlMapping(req => { return HttpResponse.Create(enumInfinite(), "text/plain"); }, path: "/infinite-and-slow")
@@ -38,8 +38,8 @@ public void TestMidResponseSocketClosure()
3838
{
3939
for (int i = 0; i < 20; i++)
4040
{
41-
TcpClient cl = new TcpClient();
42-
cl.Connect("localhost", TestHelpers.Port);
41+
TcpClient cl = new();
42+
cl.Connect("localhost", TestHelpers.Port + 1);
4343
cl.ReceiveTimeout = 1000; // 1 sec
4444
Socket sck = cl.Client;
4545
sck.Send("GET /infinite-and-slow HTTP/1.1\r\nHost: localhost\r\nConnection: keep-alive\r\n\r\n".ToUtf8());
@@ -65,7 +65,7 @@ public void TestMidResponseSocketClosure()
6565
[TestMethod, Timeout(60 * 1000, CooperativeCancellation = true)]
6666
public void TestHalfOpenConnection()
6767
{
68-
var instance = new HttpServer(TestHelpers.Port, new HttpServerOptions { OutputExceptionInformation = true });
68+
var instance = new HttpServer(TestHelpers.Port + 2, new HttpServerOptions { OutputExceptionInformation = true });
6969
instance.Handler = req => HttpResponse.PlainText(" thingy stuff ");
7070
try
7171
{
@@ -75,7 +75,7 @@ public void TestHalfOpenConnection()
7575
using (var cl = new TcpClient())
7676
{
7777
cl.ReceiveTimeout = 1000; // 1 sec
78-
cl.Connect("localhost", TestHelpers.Port);
78+
cl.Connect("localhost", TestHelpers.Port + 2);
7979
cl.Client.Send("GET /static HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n".ToUtf8());
8080
cl.Client.Shutdown(SocketShutdown.Send);
8181
var response = Encoding.UTF8.GetString(cl.Client.ReceiveAllBytes());
@@ -88,7 +88,7 @@ public void TestHalfOpenConnection()
8888
// An incomplete request ending in a half closed connection
8989
using (var cl = new TcpClient())
9090
{
91-
cl.Connect("localhost", TestHelpers.Port);
91+
cl.Connect("localhost", TestHelpers.Port + 2);
9292
cl.Client.Send("GET /static HTTP/1.1\r\nHost: localhost\r\nConnection: close".ToUtf8());
9393
cl.Client.Shutdown(SocketShutdown.Send);
9494
var response = Encoding.UTF8.GetString(cl.Client.ReceiveAllBytes());
@@ -98,7 +98,7 @@ public void TestHalfOpenConnection()
9898
// A malformed request ending in a half closed connection
9999
using (var cl = new TcpClient())
100100
{
101-
cl.Connect("localhost", TestHelpers.Port);
101+
cl.Connect("localhost", TestHelpers.Port + 2);
102102
cl.Client.Send("xz".ToUtf8());
103103
cl.Client.Shutdown(SocketShutdown.Send);
104104
var response = Encoding.UTF8.GetString(cl.Client.ReceiveAllBytes());

Tests/ErrorHandlerTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ public sealed class ErrorHandlerTests
1515
[TestMethod, Timeout(60 * 1000, CooperativeCancellation = true)]
1616
public void TestErrorHandlerExceptions()
1717
{
18-
var instance = new HttpServer(TestHelpers.Port, new HttpServerOptions { OutputExceptionInformation = true });
18+
var instance = new HttpServer(TestHelpers.Port + 3, new HttpServerOptions { OutputExceptionInformation = true });
1919
try
2020
{
2121
instance.StartListening();
2222

2323
var getResponse = Ut.Lambda(() =>
2424
{
25-
TcpClient cl = new TcpClient();
26-
cl.Connect("localhost", TestHelpers.Port);
25+
TcpClient cl = new();
26+
cl.Connect("localhost", TestHelpers.Port + 3);
2727
cl.ReceiveTimeout = 1000; // 1 sec
2828
cl.Client.Send("GET /static HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n".ToUtf8());
2929
var response = Encoding.UTF8.GetString(cl.Client.ReceiveAllBytes());
@@ -92,8 +92,8 @@ public void TestErrorHandlerExceptions()
9292
// Test that a malformed request passes through the error handler
9393
instance.ErrorHandler = (req, ex) => { storedEx = ex; throw new HttpException(HttpStatusCode._204_NoContent); };
9494
{
95-
TcpClient cl = new TcpClient();
96-
cl.Connect("localhost", TestHelpers.Port);
95+
TcpClient cl = new();
96+
cl.Connect("localhost", TestHelpers.Port + 3);
9797
cl.ReceiveTimeout = 1000; // 1 sec
9898
cl.Client.Send("xz\r\n\r\n".ToUtf8());
9999
var response = Encoding.UTF8.GetString(cl.Client.ReceiveAllBytes());
@@ -139,7 +139,7 @@ private IEnumerable<string> enumerableWithException(Exception exception)
139139
[TestMethod, Timeout(60 * 1000, CooperativeCancellation = true)]
140140
public void TestErrorHandlerAndCleanUp()
141141
{
142-
var instance = new HttpServer(TestHelpers.Port);
142+
var instance = new HttpServer(TestHelpers.Port + 4);
143143
try
144144
{
145145
bool errorHandlerCalled = false;
@@ -163,7 +163,7 @@ public void TestErrorHandlerAndCleanUp()
163163
};
164164

165165
TcpClient cl = new TcpClient();
166-
cl.Connect("localhost", TestHelpers.Port);
166+
cl.Connect("localhost", TestHelpers.Port + 4);
167167
cl.ReceiveTimeout = 1000000; // 1000 sec
168168
cl.Client.Send("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n".ToUtf8());
169169
var response = Encoding.UTF8.GetString(cl.Client.ReceiveAllBytes());

0 commit comments

Comments
 (0)