Series: Angelia
java
78 lines
· Updated 2026-09-27
ProtocolTest.java
Angelia/Java/Week_6_7/code/08_chat_protocol/ProtocolTest.java
/**
* 協定解析器的測試(用 assert;也可以改寫成 JUnit)。
*
* 執行:javac *.java && java -ea ProtocolTest
*/
public class ProtocolTest {
/** 「往返」性質:任何合法訊息 encode 之後再 parse,必須得到相等的物件。 */
static void roundTrip(ChatMessage m) {
String line = ChatMessage.encode(m);
ChatMessage back = ChatMessage.parse(line);
assert back.equals(m) : "往返失敗:" + m + " → \"" + line + "\" → " + back;
System.out.println(" " + String.format("%-40s", line) + " ⇄ " + back);
}
static void expectError(String line) {
try {
ChatMessage m = ChatMessage.parse(line);
assert false : "應該要失敗卻解析成功:" + line + " → " + m;
} catch (ChatMessage.ProtocolException e) {
System.out.println(" \"" + line + "\" → 拒絕:" + e.getMessage());
}
}
public static void main(String[] args) throws java.io.IOException {
System.out.println("=== 往返測試(record 自動有 equals,才能這樣比較)===");
roundTrip(new ChatMessage.Login("angelia"));
roundTrip(new ChatMessage.Join("general"));
roundTrip(new ChatMessage.Say("general", "hello world, with spaces!"));
roundTrip(new ChatMessage.Quit());
roundTrip(new ChatMessage.Ok(""));
roundTrip(new ChatMessage.Ok("welcome angelia"));
roundTrip(new ChatMessage.Err(401, "login first"));
roundTrip(new ChatMessage.Msg("general", "bob", "多位元組字元也沒問題:café 日本語"));
roundTrip(new ChatMessage.Bye());
System.out.println("=== 不合法的輸入必須被拒絕,而不是默默接受 ===");
expectError("");
expectError("DANCE");
expectError("LOGIN");
expectError("LOGIN two words");
expectError("SAY general");
expectError("ERR notanumber oops");
expectError("MSG general bob");
System.out.println("=== 指令不分大小寫 ===");
assert ChatMessage.parse("login angelia").equals(new ChatMessage.Login("angelia"));
System.out.println("=== 用 FakeChatClient 測 UI 邏輯(不需要網路)===");
StringBuilder screen = new StringBuilder();
ChatClient client = new FakeChatClient();
client.addListener(new ChatClient.Listener() { // 匿名類別實作監聽者介面
public void onMessage(String room, String from, String text) {
screen.append("[").append(room).append("] ").append(from).append(": ").append(text).append("\n");
}
public void onError(int code, String text) {
screen.append("ERROR ").append(code).append(": ").append(text).append("\n");
}
public void onDisconnected() {
screen.append("(disconnected)\n");
}
});
client.connect("localhost", 5555, "angelia");
client.say("general", "hi"); // 還沒 join → 錯誤
client.join("general");
client.say("general", "hi again");
client.disconnect();
System.out.print(screen);
assert screen.toString().equals(
"ERROR 403: not in room general\n[general] angelia: hi again\n(disconnected)\n");
try {
client.say("general", "after disconnect");
} catch (IllegalStateException e) {
System.out.println(" 斷線後操作 → IllegalStateException:" + e.getMessage());
}
System.out.println("全部通過。");
}
}
Related articles
Angelia
haskell
Updated 2026-02-03
Adding.hs
Adding.hs — haskell source code from the Angelia learning materials (Angelia/Haskell/Adding.hs).
Read article →
Angelia
haskell
Updated 2026-02-03
Area.hs
Area.hs — haskell source code from the Angelia learning materials (Angelia/Haskell/Area.hs).
Read article →
Angelia
haskell
Updated 2026-02-03
Chess.hs
Chess.hs — haskell source code from the Angelia learning materials (Angelia/Haskell/Chess.hs).
Read article →
Angelia
haskell
Updated 2026-02-03
ChessGraphics.hs
ChessGraphics.hs — haskell source code from the Angelia learning materials (Angelia/Haskell/ChessGraphics.hs).
Read article →
Angelia
haskell
Updated 2026-02-03
Examples.hs
Examples.hs — haskell source code from the Angelia learning materials (Angelia/Haskell/Examples.hs).
Read article →
Angelia
haskell
Updated 2026-02-03
Fibonacci.hs
Fibonacci.hs — haskell source code from the Angelia learning materials (Angelia/Haskell/Fibonacci.hs).
Read article →