yeg
4 天以前 99fd4ccdf5d410e78a3785375036c34a2132ee04
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package com.battery.device.bts;
 
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import org.junit.jupiter.api.Test;
 
/**
 * 远程命名管道传输的行为:分帧按实测的 {@code \n\n}、只读白名单在发出前生效。
 * 分帧用普通文件驱动 {@link NewareBtsPipeTransport#readFrame} —— 生产方法读写的就是 {@code RandomAccessFile},
 * 换掉的是对端是不是管道,帧扫描这一段完全一致。
 */
class NewareBtsPipeTransportTest
{
    private static final int MAX = 1024;
 
    @Test
    void returnsPayloadUpToThePlainDoubleNewlineTerminator() throws Exception
    {
        assertEquals("<bts><cmd>getdevinfo_resp</cmd></bts>", frame("<bts><cmd>getdevinfo_resp</cmd></bts>\n\n"));
    }
 
    @Test
    void ignoresBytesAfterTheTerminator() throws Exception
    {
        assertEquals("first", frame("first\n\nsecond\n\n"));
    }
 
    @Test
    void keepsTerminatorPrefixThatBelongsToThePayload() throws Exception
    {
        // 正文里出现结束符的前缀时要原样补回,不能把它吃掉。
        assertEquals("a\n#b", frame("a\n#b\n\n"));
    }
 
    @Test
    void doesNotTreatDocumentedHashTerminatorAsAnEnd() throws Exception
    {
        // 协议文档给的 #\r\n 现场实测无效;照它写就永远读不到帧尾,这里以"读到尾也没结束"表示这一点。
        assertThrows(EOFException.class, () -> frame("<bts/>#\r\n"));
    }
 
    @Test
    void rejectsPayloadBeyondTheLimit() throws Exception
    {
        assertThrows(IOException.class, () -> frameRaw("123456789\n\n", 8));
    }
 
    @Test
    void allowsPayloadExactlyAtTheLimit() throws Exception
    {
        assertEquals("12345678", frameRaw("12345678\n\n", 8));
    }
 
    @Test
    void rejectsEveryCommandOutsideTheReadOnlyWhitelistBeforeAnyTransportWork()
    {
        try (NewareBtsPipeTransport transport = new NewareBtsPipeTransport("203.0.113.1", "Administrator", "secret",
                Duration.ofSeconds(1), Duration.ofSeconds(1)))
        {
            // 控制命令一律不得发出:这里没有任何会话与管道,若白名单没生效就会变成连接失败而不是被拒绝。
            for (String command : new String[] {"start", "stop", "reset", "broadcaststop", "setpause",
                    "cancelpause", "goto", "chl_ctrl", "parallel", "light", "continue", "clearflag"})
            {
                BtsTransportException exception = assertThrows(BtsTransportException.class,
                        () -> transport.exchange("<bts><cmd>" + command + "</cmd></bts>"));
                assertEquals(BtsTransportException.Reason.READ_ONLY_REJECTED, exception.reason(), command);
            }
        }
    }
 
    @Test
    void acceptsTheReadOnlyWhitelistAndRejectsRequestsWithoutACommand()
    {
        try (NewareBtsPipeTransport transport = new NewareBtsPipeTransport("203.0.113.1", "Administrator", "secret",
                Duration.ofSeconds(1), Duration.ofSeconds(1)))
        {
            for (String command : new String[] {"getdevinfo", "getchlstatus", "inquire", "inquiredf", "download",
                    "downloadStepLayer", "downloadlog", "getparallel", "getpause"})
                assertEquals(command.toLowerCase(java.util.Locale.ROOT),
                        NewareBtsReadOnlyCommands.requireAllowed("<bts><cmd>" + command + "</cmd></bts>"));
 
            BtsTransportException exception = assertThrows(BtsTransportException.class,
                    () -> NewareBtsReadOnlyCommands.requireAllowed("<bts><note>没有命令</note></bts>"));
            assertEquals(BtsTransportException.Reason.READ_ONLY_REJECTED, exception.reason());
        }
    }
 
    private static String frame(String content) throws Exception
    {
        return frameRaw(content, MAX);
    }
 
    private static String frameRaw(String content, int maxPayloadBytes) throws Exception
    {
        Path file = Files.createTempFile("neware-bts-frame", ".txt");
        try
        {
            Files.write(file, content.getBytes(StandardCharsets.UTF_8));
            try (RandomAccessFile pipe = new RandomAccessFile(file.toFile(), "r"))
            {
                return new String(NewareBtsPipeTransport.readFrame(pipe, maxPayloadBytes), StandardCharsets.UTF_8);
            }
        }
        finally
        {
            Files.deleteIfExists(file);
        }
    }
}