@@ -0,0 +1,233 @@ | |||||
using System; | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using Shadowsocks.Encryption; | |||||
using System.Threading; | |||||
using System.Collections.Generic; | |||||
using Shadowsocks.Encryption.Stream; | |||||
using System.Diagnostics; | |||||
namespace Shadowsocks.Test | |||||
{ | |||||
[TestClass] | |||||
public class CryptographyTest | |||||
{ | |||||
[TestMethod] | |||||
public void TestMD5() | |||||
{ | |||||
for (int len = 1; len < 64; len++) | |||||
{ | |||||
System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); | |||||
byte[] bytes = new byte[len]; | |||||
var random = new Random(); | |||||
random.NextBytes(bytes); | |||||
string md5str = Convert.ToBase64String(md5.ComputeHash(bytes)); | |||||
string md5str2 = Convert.ToBase64String(MbedTLS.MD5(bytes)); | |||||
Assert.IsTrue(md5str == md5str2); | |||||
} | |||||
} | |||||
private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor) | |||||
{ | |||||
RNG.Reload(); | |||||
byte[] plain = new byte[16384]; | |||||
byte[] cipher = new byte[plain.Length + 16]; | |||||
byte[] plain2 = new byte[plain.Length + 16]; | |||||
int outLen = 0; | |||||
int outLen2 = 0; | |||||
var random = new Random(); | |||||
random.NextBytes(plain); | |||||
encryptor.Encrypt(plain, plain.Length, cipher, out outLen); | |||||
decryptor.Decrypt(cipher, outLen, plain2, out outLen2); | |||||
Assert.AreEqual(plain.Length, outLen2); | |||||
for (int j = 0; j < plain.Length; j++) | |||||
{ | |||||
Assert.AreEqual(plain[j], plain2[j]); | |||||
} | |||||
encryptor.Encrypt(plain, 1000, cipher, out outLen); | |||||
decryptor.Decrypt(cipher, outLen, plain2, out outLen2); | |||||
Assert.AreEqual(1000, outLen2); | |||||
for (int j = 0; j < outLen2; j++) | |||||
{ | |||||
Assert.AreEqual(plain[j], plain2[j]); | |||||
} | |||||
encryptor.Encrypt(plain, 12333, cipher, out outLen); | |||||
decryptor.Decrypt(cipher, outLen, plain2, out outLen2); | |||||
Assert.AreEqual(12333, outLen2); | |||||
for (int j = 0; j < outLen2; j++) | |||||
{ | |||||
Assert.AreEqual(plain[j], plain2[j]); | |||||
} | |||||
} | |||||
private static bool encryptionFailed = false; | |||||
private static object locker = new object(); | |||||
[TestMethod] | |||||
public void TestMbedTLSEncryption() | |||||
{ | |||||
encryptionFailed = false; | |||||
// run it once before the multi-threading test to initialize global tables | |||||
RunSingleMbedTLSEncryptionThread(); | |||||
List<Thread> threads = new List<Thread>(); | |||||
for (int i = 0; i < 10; i++) | |||||
{ | |||||
Thread t = new Thread(new ThreadStart(RunSingleMbedTLSEncryptionThread)); | |||||
threads.Add(t); | |||||
t.Start(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
private void RunSingleMbedTLSEncryptionThread() | |||||
{ | |||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
IEncryptor encryptor; | |||||
IEncryptor decryptor; | |||||
encryptor = new StreamMbedTLSEncryptor("aes-256-cfb", "barfoo!"); | |||||
decryptor = new StreamMbedTLSEncryptor("aes-256-cfb", "barfoo!"); | |||||
RunEncryptionRound(encryptor, decryptor); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void TestRC4Encryption() | |||||
{ | |||||
encryptionFailed = false; | |||||
// run it once before the multi-threading test to initialize global tables | |||||
RunSingleRC4EncryptionThread(); | |||||
List<Thread> threads = new List<Thread>(); | |||||
for (int i = 0; i < 10; i++) | |||||
{ | |||||
Thread t = new Thread(new ThreadStart(RunSingleRC4EncryptionThread)); | |||||
threads.Add(t); | |||||
t.Start(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
private void RunSingleRC4EncryptionThread() | |||||
{ | |||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
var random = new Random(); | |||||
IEncryptor encryptor; | |||||
IEncryptor decryptor; | |||||
encryptor = new StreamMbedTLSEncryptor("rc4-md5", "barfoo!"); | |||||
decryptor = new StreamMbedTLSEncryptor("rc4-md5", "barfoo!"); | |||||
RunEncryptionRound(encryptor, decryptor); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void TestSodiumEncryption() | |||||
{ | |||||
encryptionFailed = false; | |||||
// run it once before the multi-threading test to initialize global tables | |||||
RunSingleSodiumEncryptionThread(); | |||||
List<Thread> threads = new List<Thread>(); | |||||
for (int i = 0; i < 10; i++) | |||||
{ | |||||
Thread t = new Thread(new ThreadStart(RunSingleSodiumEncryptionThread)); | |||||
threads.Add(t); | |||||
t.Start(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
private void RunSingleSodiumEncryptionThread() | |||||
{ | |||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
var random = new Random(); | |||||
IEncryptor encryptor; | |||||
IEncryptor decryptor; | |||||
encryptor = new StreamSodiumEncryptor("salsa20", "barfoo!"); | |||||
decryptor = new StreamSodiumEncryptor("salsa20", "barfoo!"); | |||||
RunEncryptionRound(encryptor, decryptor); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void TestOpenSSLEncryption() | |||||
{ | |||||
encryptionFailed = false; | |||||
// run it once before the multi-threading test to initialize global tables | |||||
RunSingleOpenSSLEncryptionThread(); | |||||
List<Thread> threads = new List<Thread>(); | |||||
for (int i = 0; i < 10; i++) | |||||
{ | |||||
Thread t = new Thread(new ThreadStart(RunSingleOpenSSLEncryptionThread)); | |||||
threads.Add(t); | |||||
t.Start(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
private void RunSingleOpenSSLEncryptionThread() | |||||
{ | |||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
var random = new Random(); | |||||
IEncryptor encryptor; | |||||
IEncryptor decryptor; | |||||
encryptor = new StreamOpenSSLEncryptor("aes-256-cfb", "barfoo!"); | |||||
decryptor = new StreamOpenSSLEncryptor("aes-256-cfb", "barfoo!"); | |||||
RunEncryptionRound(encryptor, decryptor); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -48,7 +48,7 @@ using System.Runtime.CompilerServices; | |||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using System.Text; | using System.Text; | ||||
namespace test | |||||
namespace Shadowsocks.Test | |||||
{ | { | ||||
static class ProcessEnvironment | static class ProcessEnvironment | ||||
{ | { | ||||
@@ -0,0 +1,175 @@ | |||||
using System; | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using System.Threading; | |||||
using System.Collections.Generic; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Controller.Service; | |||||
using System.Diagnostics; | |||||
using System.Net; | |||||
namespace Shadowsocks.Test | |||||
{ | |||||
[TestClass] | |||||
public class Sip003PluginTest | |||||
{ | |||||
[TestMethod] | |||||
public void Sip003PluginSupport() | |||||
{ | |||||
string fake_plugin = "ftp"; | |||||
var NoPlugin = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb" | |||||
}); | |||||
RunPluginSupportTest( | |||||
NoPlugin, | |||||
"", | |||||
"", | |||||
"", | |||||
"192.168.100.1", | |||||
8888); | |||||
var Plugin = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin | |||||
}); | |||||
RunPluginSupportTest( | |||||
Plugin, | |||||
fake_plugin, | |||||
"", | |||||
"", | |||||
"192.168.100.1", | |||||
8888); | |||||
var PluginWithOpts = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_opts = "_option" | |||||
}); | |||||
RunPluginSupportTest( | |||||
PluginWithOpts, | |||||
fake_plugin, | |||||
"_option", | |||||
"", | |||||
"192.168.100.1", | |||||
8888); | |||||
var PluginWithArgs = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_args = "_test" | |||||
}); | |||||
RunPluginSupportTest( | |||||
PluginWithArgs, | |||||
fake_plugin, | |||||
"", | |||||
"_test", | |||||
"192.168.100.1", | |||||
8888); | |||||
var PluginWithOptsAndArgs = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_opts = "_option", | |||||
plugin_args = "_test" | |||||
}); | |||||
RunPluginSupportTest( | |||||
PluginWithOptsAndArgs, | |||||
fake_plugin, | |||||
"_option", | |||||
"_test", | |||||
"192.168.100.1", | |||||
8888); | |||||
var PluginWithArgsReplaced = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_args = "_test,%SS_REMOTE_HOST%" | |||||
}); | |||||
RunPluginSupportTest( | |||||
PluginWithArgsReplaced, | |||||
fake_plugin, | |||||
"", | |||||
"_test,192.168.100.1", | |||||
"192.168.100.1", | |||||
8888); | |||||
var PluginWithOptsAndArgsReplaced = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_opts = "_option", | |||||
plugin_args = "_test,%SS_REMOTE_HOST%,%SS_PLUGIN_OPTIONS%" | |||||
}); | |||||
RunPluginSupportTest( | |||||
PluginWithOptsAndArgsReplaced, | |||||
fake_plugin, | |||||
"_option", | |||||
"_test,192.168.100.1,_option", | |||||
"192.168.100.1", | |||||
8888); | |||||
} | |||||
private static void RunPluginSupportTest(Sip003Plugin plugin, string pluginName, string pluginOpts, string pluginArgs, string serverAddress, int serverPort) | |||||
{ | |||||
if (string.IsNullOrWhiteSpace(pluginName)) return; | |||||
plugin.StartIfNeeded(); | |||||
Process[] processes = Process.GetProcessesByName(pluginName); | |||||
Assert.AreEqual(processes.Length, 1); | |||||
Process p = processes[0]; | |||||
var penv = ProcessEnvironment.ReadEnvironmentVariables(p); | |||||
var pcmd = ProcessEnvironment.GetCommandLine(p).Trim(); | |||||
pcmd = pcmd.IndexOf(' ') >= 0 ? pcmd.Substring(pcmd.IndexOf(' ') + 1) : ""; | |||||
Assert.AreEqual(penv["SS_REMOTE_HOST"], serverAddress); | |||||
Assert.AreEqual(penv["SS_REMOTE_PORT"], serverPort.ToString()); | |||||
Assert.AreEqual(penv["SS_LOCAL_HOST"], IPAddress.Loopback.ToString()); | |||||
int _ignored; | |||||
Assert.IsTrue(int.TryParse(penv["SS_LOCAL_PORT"], out _ignored)); | |||||
Assert.AreEqual(penv["SS_PLUGIN_OPTIONS"], pluginOpts); | |||||
Assert.AreEqual(pcmd, pluginArgs); | |||||
plugin.Dispose(); | |||||
for (int i = 0; i < 50; i++) | |||||
{ | |||||
if (Process.GetProcessesByName(pluginName).Length == 0) return; | |||||
Thread.Sleep(50); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,19 +1,15 @@ | |||||
using System; | using System; | ||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using Shadowsocks.Encryption; | |||||
using GlobalHotKey; | using GlobalHotKey; | ||||
using System.Windows.Input; | using System.Windows.Input; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using Shadowsocks.Controller.Hotkeys; | using Shadowsocks.Controller.Hotkeys; | ||||
using Shadowsocks.Encryption.Stream; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Controller.Service; | |||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.Net; | |||||
namespace test | |||||
namespace Shadowsocks.Test | |||||
{ | { | ||||
[TestClass] | [TestClass] | ||||
public class UnitTest | public class UnitTest | ||||
@@ -57,592 +53,5 @@ namespace test | |||||
Assert.IsTrue(testKey3 != null && testKey3.Equals(new HotKey(Key.NumPad7, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); | Assert.IsTrue(testKey3 != null && testKey3.Equals(new HotKey(Key.NumPad7, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); | ||||
} | } | ||||
[TestMethod] | |||||
public void TestMD5() | |||||
{ | |||||
for (int len = 1; len < 64; len++) | |||||
{ | |||||
System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); | |||||
byte[] bytes = new byte[len]; | |||||
var random = new Random(); | |||||
random.NextBytes(bytes); | |||||
string md5str = Convert.ToBase64String(md5.ComputeHash(bytes)); | |||||
string md5str2 = Convert.ToBase64String(MbedTLS.MD5(bytes)); | |||||
Assert.IsTrue(md5str == md5str2); | |||||
} | |||||
} | |||||
private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor) | |||||
{ | |||||
RNG.Reload(); | |||||
byte[] plain = new byte[16384]; | |||||
byte[] cipher = new byte[plain.Length + 16]; | |||||
byte[] plain2 = new byte[plain.Length + 16]; | |||||
int outLen = 0; | |||||
int outLen2 = 0; | |||||
var random = new Random(); | |||||
random.NextBytes(plain); | |||||
encryptor.Encrypt(plain, plain.Length, cipher, out outLen); | |||||
decryptor.Decrypt(cipher, outLen, plain2, out outLen2); | |||||
Assert.AreEqual(plain.Length, outLen2); | |||||
for (int j = 0; j < plain.Length; j++) | |||||
{ | |||||
Assert.AreEqual(plain[j], plain2[j]); | |||||
} | |||||
encryptor.Encrypt(plain, 1000, cipher, out outLen); | |||||
decryptor.Decrypt(cipher, outLen, plain2, out outLen2); | |||||
Assert.AreEqual(1000, outLen2); | |||||
for (int j = 0; j < outLen2; j++) | |||||
{ | |||||
Assert.AreEqual(plain[j], plain2[j]); | |||||
} | |||||
encryptor.Encrypt(plain, 12333, cipher, out outLen); | |||||
decryptor.Decrypt(cipher, outLen, plain2, out outLen2); | |||||
Assert.AreEqual(12333, outLen2); | |||||
for (int j = 0; j < outLen2; j++) | |||||
{ | |||||
Assert.AreEqual(plain[j], plain2[j]); | |||||
} | |||||
} | |||||
private static bool encryptionFailed = false; | |||||
private static object locker = new object(); | |||||
[TestMethod] | |||||
public void TestMbedTLSEncryption() | |||||
{ | |||||
// run it once before the multi-threading test to initialize global tables | |||||
RunSingleMbedTLSEncryptionThread(); | |||||
List<Thread> threads = new List<Thread>(); | |||||
for (int i = 0; i < 10; i++) | |||||
{ | |||||
Thread t = new Thread(new ThreadStart(RunSingleMbedTLSEncryptionThread)); | |||||
threads.Add(t); | |||||
t.Start(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
private void RunSingleMbedTLSEncryptionThread() | |||||
{ | |||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
IEncryptor encryptor; | |||||
IEncryptor decryptor; | |||||
encryptor = new StreamMbedTLSEncryptor("aes-256-cfb", "barfoo!"); | |||||
decryptor = new StreamMbedTLSEncryptor("aes-256-cfb", "barfoo!"); | |||||
RunEncryptionRound(encryptor, decryptor); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void TestRC4Encryption() | |||||
{ | |||||
// run it once before the multi-threading test to initialize global tables | |||||
RunSingleRC4EncryptionThread(); | |||||
List<Thread> threads = new List<Thread>(); | |||||
for (int i = 0; i < 10; i++) | |||||
{ | |||||
Thread t = new Thread(new ThreadStart(RunSingleRC4EncryptionThread)); | |||||
threads.Add(t); | |||||
t.Start(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
private void RunSingleRC4EncryptionThread() | |||||
{ | |||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
var random = new Random(); | |||||
IEncryptor encryptor; | |||||
IEncryptor decryptor; | |||||
encryptor = new StreamMbedTLSEncryptor("rc4-md5", "barfoo!"); | |||||
decryptor = new StreamMbedTLSEncryptor("rc4-md5", "barfoo!"); | |||||
RunEncryptionRound(encryptor, decryptor); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void TestSodiumEncryption() | |||||
{ | |||||
// run it once before the multi-threading test to initialize global tables | |||||
RunSingleSodiumEncryptionThread(); | |||||
List<Thread> threads = new List<Thread>(); | |||||
for (int i = 0; i < 10; i++) | |||||
{ | |||||
Thread t = new Thread(new ThreadStart(RunSingleSodiumEncryptionThread)); | |||||
threads.Add(t); | |||||
t.Start(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
private void RunSingleSodiumEncryptionThread() | |||||
{ | |||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
var random = new Random(); | |||||
IEncryptor encryptor; | |||||
IEncryptor decryptor; | |||||
encryptor = new StreamSodiumEncryptor("salsa20", "barfoo!"); | |||||
decryptor = new StreamSodiumEncryptor("salsa20", "barfoo!"); | |||||
RunEncryptionRound(encryptor, decryptor); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void TestOpenSSLEncryption() | |||||
{ | |||||
// run it once before the multi-threading test to initialize global tables | |||||
RunSingleOpenSSLEncryptionThread(); | |||||
List<Thread> threads = new List<Thread>(); | |||||
for (int i = 0; i < 10; i++) | |||||
{ | |||||
Thread t = new Thread(new ThreadStart(RunSingleOpenSSLEncryptionThread)); | |||||
threads.Add(t); | |||||
t.Start(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
RNG.Close(); | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
private void RunSingleOpenSSLEncryptionThread() | |||||
{ | |||||
try | |||||
{ | |||||
for (int i = 0; i < 100; i++) | |||||
{ | |||||
var random = new Random(); | |||||
IEncryptor encryptor; | |||||
IEncryptor decryptor; | |||||
encryptor = new StreamOpenSSLEncryptor("aes-256-cfb", "barfoo!"); | |||||
decryptor = new StreamOpenSSLEncryptor("aes-256-cfb", "barfoo!"); | |||||
RunEncryptionRound(encryptor, decryptor); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void ParseAndGenerateShadowsocksUrl() | |||||
{ | |||||
var server = new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb" | |||||
}; | |||||
var serverCanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4"; | |||||
var server2 = new Server | |||||
{ | |||||
server = "192.168.1.1", | |||||
server_port = 8388, | |||||
password = "test", | |||||
method = "bf-cfb" | |||||
}; | |||||
var server2CanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA=="; | |||||
var serverWithRemark = new Server | |||||
{ | |||||
server = server.server, | |||||
server_port = server.server_port, | |||||
password = server.password, | |||||
method = server.method, | |||||
remarks = "example-server" | |||||
}; | |||||
var serverWithRemarkCanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4#example-server"; | |||||
var server2WithRemark = new Server | |||||
{ | |||||
server = server2.server, | |||||
server_port = server2.server_port, | |||||
password = server2.password, | |||||
method = server2.method, | |||||
remarks = "example-server" | |||||
}; | |||||
var server2WithRemarkCanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA==#example-server"; | |||||
var serverWithPlugin = new Server | |||||
{ | |||||
server = server.server, | |||||
server_port = server.server_port, | |||||
password = server.password, | |||||
method = server.method, | |||||
plugin = "obfs-local", | |||||
plugin_opts = "obfs=http;obfs-host=google.com" | |||||
}; | |||||
var serverWithPluginCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com"; | |||||
var server2WithPlugin = new Server | |||||
{ | |||||
server = server2.server, | |||||
server_port = server2.server_port, | |||||
password = server2.password, | |||||
method = server2.method, | |||||
plugin = "obfs-local", | |||||
plugin_opts = "obfs=http;obfs-host=google.com" | |||||
}; | |||||
var server2WithPluginCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com"; | |||||
var serverWithPluginAndRemark = new Server | |||||
{ | |||||
server = server.server, | |||||
server_port = server.server_port, | |||||
password = server.password, | |||||
method = server.method, | |||||
plugin = serverWithPlugin.plugin, | |||||
plugin_opts = serverWithPlugin.plugin_opts, | |||||
remarks = serverWithRemark.remarks | |||||
}; | |||||
var serverWithPluginAndRemarkCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com#example-server"; | |||||
var server2WithPluginAndRemark = new Server | |||||
{ | |||||
server = server2.server, | |||||
server_port = server2.server_port, | |||||
password = server2.password, | |||||
method = server2.method, | |||||
plugin = server2WithPlugin.plugin, | |||||
plugin_opts = server2WithPlugin.plugin_opts, | |||||
remarks = server2WithRemark.remarks | |||||
}; | |||||
var server2WithPluginAndRemarkCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com#example-server"; | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
serverCanonUrl, | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4/", | |||||
serverWithRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4/#example-server"), | |||||
new[] | |||||
{ | |||||
server, | |||||
server, | |||||
serverWithRemark, | |||||
serverWithRemark | |||||
}); | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
server2CanonUrl, | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA==/", | |||||
server2WithRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA==/#example-server"), | |||||
new[] | |||||
{ | |||||
server2, | |||||
server2, | |||||
server2WithRemark, | |||||
server2WithRemark | |||||
}); | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888", | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888#example-server", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/#example-server", | |||||
serverWithPluginCanonUrl, | |||||
serverWithPluginAndRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com&unsupported=1#example-server"), | |||||
new[] | |||||
{ | |||||
server, | |||||
server, | |||||
serverWithRemark, | |||||
serverWithRemark, | |||||
serverWithPlugin, | |||||
serverWithPluginAndRemark, | |||||
serverWithPluginAndRemark | |||||
}); | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388", | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388#example-server", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/#example-server", | |||||
server2WithPluginCanonUrl, | |||||
server2WithPluginAndRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com&unsupported=1#example-server"), | |||||
new[] | |||||
{ | |||||
server2, | |||||
server2, | |||||
server2WithRemark, | |||||
server2WithRemark, | |||||
server2WithPlugin, | |||||
server2WithPluginAndRemark, | |||||
server2WithPluginAndRemark | |||||
}); | |||||
var generateUrlCases = new Dictionary<string, Server> | |||||
{ | |||||
[serverCanonUrl] = server, | |||||
[serverWithRemarkCanonUrl] = serverWithRemark, | |||||
[serverWithPluginCanonUrl] = serverWithPlugin, | |||||
[serverWithPluginAndRemarkCanonUrl] = serverWithPluginAndRemark | |||||
}; | |||||
RunGenerateShadowsocksUrlTest(generateUrlCases); | |||||
} | |||||
private static void RunParseShadowsocksUrlTest(string testCase, IReadOnlyList<Server> expected) | |||||
{ | |||||
var actual = Server.GetServers(testCase); | |||||
if (actual.Count != expected.Count) | |||||
{ | |||||
Assert.Fail("Wrong number of configs. Expected: {0}. Actual: {1}", expected.Count, actual.Count); | |||||
} | |||||
for (int i = 0; i < expected.Count; i++) | |||||
{ | |||||
var expectedServer = expected[i]; | |||||
var actualServer = actual[i]; | |||||
Assert.AreEqual(expectedServer.server, actualServer.server); | |||||
Assert.AreEqual(expectedServer.server_port, actualServer.server_port); | |||||
Assert.AreEqual(expectedServer.password, actualServer.password); | |||||
Assert.AreEqual(expectedServer.method, actualServer.method); | |||||
Assert.AreEqual(expectedServer.plugin, actualServer.plugin); | |||||
Assert.AreEqual(expectedServer.plugin_opts, actualServer.plugin_opts); | |||||
Assert.AreEqual(expectedServer.remarks, actualServer.remarks); | |||||
Assert.AreEqual(expectedServer.timeout, actualServer.timeout); | |||||
} | |||||
} | |||||
private static void RunGenerateShadowsocksUrlTest(IReadOnlyDictionary<string, Server> testCases) | |||||
{ | |||||
foreach (var testCase in testCases) | |||||
{ | |||||
string expected = testCase.Key; | |||||
Server config = testCase.Value; | |||||
var actual = ShadowsocksController.GetServerURL(config); | |||||
Assert.AreEqual(expected, actual); | |||||
} | |||||
} | |||||
[TestMethod] | |||||
public void PluginSupport() | |||||
{ | |||||
string fake_plugin = "ftp"; | |||||
var NoPlugin = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb" | |||||
}); | |||||
RunPluginSupportTest( | |||||
NoPlugin, | |||||
"", | |||||
"", | |||||
"", | |||||
"192.168.100.1", | |||||
8888); | |||||
var Plugin = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin | |||||
}); | |||||
RunPluginSupportTest( | |||||
Plugin, | |||||
fake_plugin, | |||||
"", | |||||
"", | |||||
"192.168.100.1", | |||||
8888); | |||||
var PluginWithOpts = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_opts = "_option" | |||||
}); | |||||
RunPluginSupportTest( | |||||
PluginWithOpts, | |||||
fake_plugin, | |||||
"_option", | |||||
"", | |||||
"192.168.100.1", | |||||
8888); | |||||
var PluginWithArgs = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_args = "_test" | |||||
}); | |||||
RunPluginSupportTest( | |||||
PluginWithArgs, | |||||
fake_plugin, | |||||
"", | |||||
"_test", | |||||
"192.168.100.1", | |||||
8888); | |||||
var PluginWithOptsAndArgs = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_opts = "_option", | |||||
plugin_args = "_test" | |||||
}); | |||||
RunPluginSupportTest( | |||||
PluginWithOptsAndArgs, | |||||
fake_plugin, | |||||
"_option", | |||||
"_test", | |||||
"192.168.100.1", | |||||
8888); | |||||
var PluginWithArgsReplaced = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_args = "_test,%SS_REMOTE_HOST%" | |||||
}); | |||||
RunPluginSupportTest( | |||||
PluginWithArgsReplaced, | |||||
fake_plugin, | |||||
"", | |||||
"_test,192.168.100.1", | |||||
"192.168.100.1", | |||||
8888); | |||||
var PluginWithOptsAndArgsReplaced = Sip003Plugin.CreateIfConfigured(new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_opts = "_option", | |||||
plugin_args = "_test,%SS_REMOTE_HOST%,%SS_PLUGIN_OPTIONS%" | |||||
}); | |||||
RunPluginSupportTest( | |||||
PluginWithOptsAndArgsReplaced, | |||||
fake_plugin, | |||||
"_option", | |||||
"_test,192.168.100.1,_option", | |||||
"192.168.100.1", | |||||
8888); | |||||
} | |||||
private static void RunPluginSupportTest(Sip003Plugin plugin, string pluginName, string pluginOpts, string pluginArgs, string serverAddress, int serverPort) | |||||
{ | |||||
if (string.IsNullOrWhiteSpace(pluginName)) return; | |||||
plugin.StartIfNeeded(); | |||||
Process[] processes = Process.GetProcessesByName(pluginName); | |||||
Assert.AreEqual(processes.Length, 1); | |||||
Process p = processes[0]; | |||||
var penv = ProcessEnvironment.ReadEnvironmentVariables(p); | |||||
var pcmd = ProcessEnvironment.GetCommandLine(p).Trim(); | |||||
pcmd = pcmd.IndexOf(' ') >= 0 ? pcmd.Substring(pcmd.IndexOf(' ') + 1) : ""; | |||||
Assert.AreEqual(penv["SS_REMOTE_HOST"], serverAddress); | |||||
Assert.AreEqual(penv["SS_REMOTE_PORT"], serverPort.ToString()); | |||||
Assert.AreEqual(penv["SS_LOCAL_HOST"], IPAddress.Loopback.ToString()); | |||||
int _ignored; | |||||
Assert.IsTrue(int.TryParse(penv["SS_LOCAL_PORT"], out _ignored)); | |||||
Assert.AreEqual(penv["SS_PLUGIN_OPTIONS"], pluginOpts); | |||||
Assert.AreEqual(pcmd, pluginArgs); | |||||
plugin.Dispose(); | |||||
for (int i = 0; i < 50; i++) | |||||
{ | |||||
if (Process.GetProcessesByName(pluginName).Length == 0) return; | |||||
Thread.Sleep(50); | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -0,0 +1,229 @@ | |||||
using System; | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using Shadowsocks.Controller; | |||||
using System.Threading; | |||||
using System.Collections.Generic; | |||||
using Shadowsocks.Model; | |||||
using System.Diagnostics; | |||||
namespace Shadowsocks.Test | |||||
{ | |||||
[TestClass] | |||||
public class UrlTest | |||||
{ | |||||
[TestMethod] | |||||
public void ParseAndGenerateShadowsocksUrl() | |||||
{ | |||||
var server = new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb" | |||||
}; | |||||
var serverCanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4"; | |||||
var server2 = new Server | |||||
{ | |||||
server = "192.168.1.1", | |||||
server_port = 8388, | |||||
password = "test", | |||||
method = "bf-cfb" | |||||
}; | |||||
var server2CanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA=="; | |||||
var serverWithRemark = new Server | |||||
{ | |||||
server = server.server, | |||||
server_port = server.server_port, | |||||
password = server.password, | |||||
method = server.method, | |||||
remarks = "example-server" | |||||
}; | |||||
var serverWithRemarkCanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4#example-server"; | |||||
var server2WithRemark = new Server | |||||
{ | |||||
server = server2.server, | |||||
server_port = server2.server_port, | |||||
password = server2.password, | |||||
method = server2.method, | |||||
remarks = "example-server" | |||||
}; | |||||
var server2WithRemarkCanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA==#example-server"; | |||||
var serverWithPlugin = new Server | |||||
{ | |||||
server = server.server, | |||||
server_port = server.server_port, | |||||
password = server.password, | |||||
method = server.method, | |||||
plugin = "obfs-local", | |||||
plugin_opts = "obfs=http;obfs-host=google.com" | |||||
}; | |||||
var serverWithPluginCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com"; | |||||
var server2WithPlugin = new Server | |||||
{ | |||||
server = server2.server, | |||||
server_port = server2.server_port, | |||||
password = server2.password, | |||||
method = server2.method, | |||||
plugin = "obfs-local", | |||||
plugin_opts = "obfs=http;obfs-host=google.com" | |||||
}; | |||||
var server2WithPluginCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com"; | |||||
var serverWithPluginAndRemark = new Server | |||||
{ | |||||
server = server.server, | |||||
server_port = server.server_port, | |||||
password = server.password, | |||||
method = server.method, | |||||
plugin = serverWithPlugin.plugin, | |||||
plugin_opts = serverWithPlugin.plugin_opts, | |||||
remarks = serverWithRemark.remarks | |||||
}; | |||||
var serverWithPluginAndRemarkCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com#example-server"; | |||||
var server2WithPluginAndRemark = new Server | |||||
{ | |||||
server = server2.server, | |||||
server_port = server2.server_port, | |||||
password = server2.password, | |||||
method = server2.method, | |||||
plugin = server2WithPlugin.plugin, | |||||
plugin_opts = server2WithPlugin.plugin_opts, | |||||
remarks = server2WithRemark.remarks | |||||
}; | |||||
var server2WithPluginAndRemarkCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com#example-server"; | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
serverCanonUrl, | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4/", | |||||
serverWithRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4/#example-server"), | |||||
new[] | |||||
{ | |||||
server, | |||||
server, | |||||
serverWithRemark, | |||||
serverWithRemark | |||||
}); | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
server2CanonUrl, | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA==/", | |||||
server2WithRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA==/#example-server"), | |||||
new[] | |||||
{ | |||||
server2, | |||||
server2, | |||||
server2WithRemark, | |||||
server2WithRemark | |||||
}); | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888", | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888#example-server", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/#example-server", | |||||
serverWithPluginCanonUrl, | |||||
serverWithPluginAndRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com&unsupported=1#example-server"), | |||||
new[] | |||||
{ | |||||
server, | |||||
server, | |||||
serverWithRemark, | |||||
serverWithRemark, | |||||
serverWithPlugin, | |||||
serverWithPluginAndRemark, | |||||
serverWithPluginAndRemark | |||||
}); | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388", | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388#example-server", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/#example-server", | |||||
server2WithPluginCanonUrl, | |||||
server2WithPluginAndRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com&unsupported=1#example-server"), | |||||
new[] | |||||
{ | |||||
server2, | |||||
server2, | |||||
server2WithRemark, | |||||
server2WithRemark, | |||||
server2WithPlugin, | |||||
server2WithPluginAndRemark, | |||||
server2WithPluginAndRemark | |||||
}); | |||||
var generateUrlCases = new Dictionary<string, Server> | |||||
{ | |||||
[serverCanonUrl] = server, | |||||
[serverWithRemarkCanonUrl] = serverWithRemark, | |||||
[serverWithPluginCanonUrl] = serverWithPlugin, | |||||
[serverWithPluginAndRemarkCanonUrl] = serverWithPluginAndRemark | |||||
}; | |||||
RunGenerateShadowsocksUrlTest(generateUrlCases); | |||||
} | |||||
private static void RunParseShadowsocksUrlTest(string testCase, IReadOnlyList<Server> expected) | |||||
{ | |||||
var actual = Server.GetServers(testCase); | |||||
if (actual.Count != expected.Count) | |||||
{ | |||||
Assert.Fail("Wrong number of configs. Expected: {0}. Actual: {1}", expected.Count, actual.Count); | |||||
} | |||||
for (int i = 0; i < expected.Count; i++) | |||||
{ | |||||
var expectedServer = expected[i]; | |||||
var actualServer = actual[i]; | |||||
Assert.AreEqual(expectedServer.server, actualServer.server); | |||||
Assert.AreEqual(expectedServer.server_port, actualServer.server_port); | |||||
Assert.AreEqual(expectedServer.password, actualServer.password); | |||||
Assert.AreEqual(expectedServer.method, actualServer.method); | |||||
Assert.AreEqual(expectedServer.plugin, actualServer.plugin); | |||||
Assert.AreEqual(expectedServer.plugin_opts, actualServer.plugin_opts); | |||||
Assert.AreEqual(expectedServer.remarks, actualServer.remarks); | |||||
Assert.AreEqual(expectedServer.timeout, actualServer.timeout); | |||||
} | |||||
} | |||||
private static void RunGenerateShadowsocksUrlTest(IReadOnlyDictionary<string, Server> testCases) | |||||
{ | |||||
foreach (var testCase in testCases) | |||||
{ | |||||
string expected = testCase.Key; | |||||
Server config = testCase.Value; | |||||
var actual = ShadowsocksController.GetServerURL(config); | |||||
Assert.AreEqual(expected, actual); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -56,6 +56,9 @@ | |||||
</Choose> | </Choose> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Compile Include="ProcessEnvironment.cs" /> | <Compile Include="ProcessEnvironment.cs" /> | ||||
<Compile Include="CryptographyTest.cs" /> | |||||
<Compile Include="Sip003PluginTest.cs" /> | |||||
<Compile Include="UrlTest.cs" /> | |||||
<Compile Include="UnitTest.cs" /> | <Compile Include="UnitTest.cs" /> | ||||
<Compile Include="Properties\AssemblyInfo.cs" /> | <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
</ItemGroup> | </ItemGroup> | ||||