5
5
using System . Collections . Generic ;
6
6
using System . Linq ;
7
7
using System . Net . Http ;
8
+ using System . Text . RegularExpressions ;
8
9
using System . Threading . Tasks ;
9
10
using NUnit . Framework ;
10
11
using Unity . Netcode . Runtime ;
@@ -15,14 +16,18 @@ namespace Unity.Netcode.RuntimeTests
15
16
{
16
17
internal class HelpUrlTests
17
18
{
18
- private static readonly HttpClient k_HttpClient = new HttpClient ( ) ;
19
+ private const string k_PackageName = "com.unity.netcode.gameobjects" ;
20
+ private static readonly HttpClient k_HttpClient = new ( ) ;
21
+
22
+ private bool m_VerboseLogging = false ;
19
23
20
24
[ UnityTest ]
21
25
public IEnumerator ValidateUrlsAreValid ( )
22
26
{
23
27
var names = new List < string > ( ) ;
24
28
var allUrls = new List < string > ( ) ;
25
29
30
+ // GetFields() can only see public strings. Ensure each HelpUrl is public.
26
31
foreach ( var constant in typeof ( HelpUrls ) . GetFields ( ) )
27
32
{
28
33
if ( constant . IsLiteral && ! constant . IsInitOnly )
@@ -31,12 +36,13 @@ public IEnumerator ValidateUrlsAreValid()
31
36
allUrls . Add ( ( string ) constant . GetValue ( null ) ) ;
32
37
}
33
38
}
34
- Debug . Log ( $ "Found { allUrls . Count } URLs") ;
39
+
40
+ VerboseLog ( $ "Found { allUrls . Count } URLs") ;
35
41
36
42
var tasks = new List < Task < bool > > ( ) ;
37
43
foreach ( var url in allUrls )
38
44
{
39
- tasks . Add ( IsRemoteFileAvailable ( url ) ) ;
45
+ tasks . Add ( AreUnityDocsAvailableAt ( url ) ) ;
40
46
}
41
47
42
48
while ( tasks . Any ( task => ! task . IsCompleted ) )
@@ -46,7 +52,42 @@ public IEnumerator ValidateUrlsAreValid()
46
52
47
53
for ( int i = 0 ; i < allUrls . Count ; i ++ )
48
54
{
49
- Assert . IsTrue ( tasks [ i ] . Result , $ "HelpUrls.{ names [ i ] } is an invalid path!") ;
55
+ Assert . IsTrue ( tasks [ i ] . Result , $ "HelpUrls.{ names [ i ] } has an invalid path! Path: { allUrls [ i ] } ") ;
56
+ }
57
+ }
58
+
59
+ private async Task < bool > AreUnityDocsAvailableAt ( string url )
60
+ {
61
+ try
62
+ {
63
+ var split = url . Split ( '#' ) ;
64
+ url = split [ 0 ] ;
65
+
66
+ var stream = await GetContentFromRemoteFile ( url ) ;
67
+
68
+ var redirectUrl = CalculateRedirectURl ( url , stream ) ;
69
+ VerboseLog ( $ "Calculated Redirect URL: { redirectUrl } ") ;
70
+
71
+ var content = await GetContentFromRemoteFile ( redirectUrl ) ;
72
+
73
+ // If original url had an anchor part (e.g. some/url.html#anchor)
74
+ if ( split . Length > 1 )
75
+ {
76
+ var anchorString = split [ 1 ] ;
77
+
78
+ // All headings will have an id with the anchorstring (e.g. <h2 id="anchor">)
79
+ if ( ! content . Contains ( $ "id=\" { anchorString } \" >") )
80
+ {
81
+ return false ;
82
+ }
83
+ }
84
+
85
+ return true ;
86
+ }
87
+ catch ( Exception e )
88
+ {
89
+ VerboseLog ( e . Message ) ;
90
+ return false ;
50
91
}
51
92
}
52
93
@@ -55,29 +96,68 @@ public IEnumerator ValidateUrlsAreValid()
55
96
/// </summary>
56
97
/// <param name="url">URL to a remote file.</param>
57
98
/// <returns>True if the file at the <paramref name="url"/> is able to be downloaded, false if the file does not exist, or if the file is restricted.</returns>
58
- private static async Task < bool > IsRemoteFileAvailable ( string url )
99
+ private async Task < string > GetContentFromRemoteFile ( string url )
59
100
{
60
101
//Checking if URI is well formed is optional
61
102
var uri = new Uri ( url ) ;
62
103
if ( ! uri . IsWellFormedOriginalString ( ) )
63
104
{
64
- Debug . LogError ( $ "URL { url } is not well formed") ;
65
- return false ;
105
+ throw new Exception ( $ "URL { url } is not well formed") ;
66
106
}
67
107
68
108
try
69
109
{
70
- using var request = new HttpRequestMessage ( HttpMethod . Head , uri ) ;
110
+ using var request = new HttpRequestMessage ( HttpMethod . Get , uri ) ;
71
111
using var response = await k_HttpClient . SendAsync ( request ) ;
112
+ if ( ! response . IsSuccessStatusCode || response . Content . Headers . ContentLength <= 0 )
113
+ {
114
+ throw new Exception ( $ "Failed to get remote file from URL { url } ") ;
115
+ }
72
116
73
- var exists = response . IsSuccessStatusCode && response . Content . Headers . ContentLength > 0 ;
74
- Debug . Log ( $ "url { url } returned status code { response . StatusCode } ") ;
75
- return exists ;
117
+ return await response . Content . ReadAsStringAsync ( ) ;
76
118
}
77
119
catch
78
120
{
79
- Debug . LogError ( $ "URL { url } request failed") ;
80
- return false ;
121
+ throw new Exception ( $ "URL { url } request failed") ;
122
+ }
123
+ }
124
+
125
+ private string CalculateRedirectURl ( string originalRequest , string content )
126
+ {
127
+ var uri = new Uri ( originalRequest ) ;
128
+ var baseRequest = $ "{ uri . Scheme } ://{ uri . Host } ";
129
+ foreach ( var segment in uri . Segments )
130
+ {
131
+ if ( segment . Contains ( k_PackageName ) )
132
+ {
133
+ break ;
134
+ }
135
+ baseRequest += segment ;
136
+ }
137
+
138
+ var subfolderRegex = new Regex ( @"[?&](\w[\w.]*)=([^?&]+)" ) . Match ( uri . Query ) ;
139
+ var subfolder = "" ;
140
+ foreach ( Group match in subfolderRegex . Groups )
141
+ {
142
+ subfolder = match . Value ;
143
+ }
144
+
145
+ string pattern = @"com.unity.netcode.gameobjects\@(\d+.\d+)" ;
146
+ var targetDestination = "" ;
147
+ foreach ( Match match in Regex . Matches ( content , pattern ) )
148
+ {
149
+ targetDestination = match . Value ;
150
+ break ;
151
+ }
152
+
153
+ return baseRequest + targetDestination + subfolder ;
154
+ }
155
+
156
+ private void VerboseLog ( string message )
157
+ {
158
+ if ( m_VerboseLogging )
159
+ {
160
+ Debug . unityLogger . Log ( message ) ;
81
161
}
82
162
}
83
163
0 commit comments