1b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair/*
2b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * Copyright 2007 the original author or authors.
3b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair *
4b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * Licensed under the Apache License, Version 2.0 (the "License");
5b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * you may not use this file except in compliance with the License.
6b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * You may obtain a copy of the License at
7b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair *
8b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair *      http://www.apache.org/licenses/LICENSE-2.0
9b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair *
10b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * Unless required by applicable law or agreed to in writing, software
11b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * distributed under the License is distributed on an "AS IS" BASIS,
12b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * See the License for the specific language governing permissions and
14b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * limitations under the License.
15b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair */
16b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismairpackage org.mockftpserver.core.command;
17b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
18b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismairimport org.apache.log4j.Logger;
19b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismairimport org.easymock.MockControl;
20b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismairimport org.mockftpserver.core.session.Session;
21b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismairimport org.mockftpserver.core.util.AssertFailedException;
22b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismairimport org.mockftpserver.test.AbstractTest;
23b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
24b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismairimport java.util.ListResourceBundle;
25b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismairimport java.util.ResourceBundle;
26b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
27b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair/**
28b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * Tests for the AbstractTrackingCommandHandler class. The class name is prefixed with an
29b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * underscore so that it is not filtered out by Maven's Surefire test plugin.
30b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair *
31b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * @author Chris Mair
32b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair * @version $Revision$ - $Date$
33b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair */
34b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismairpublic final class _AbstractTrackingCommandHandlerTest extends AbstractTest {
35b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
36b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final Logger LOG = Logger.getLogger(_AbstractTrackingCommandHandlerTest.class);
37b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final String COMMAND_NAME = "abc";
38b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final Object ARG = "123";
39b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final Object[] ARGS = {ARG};
40b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final Command COMMAND = new Command(COMMAND_NAME, EMPTY);
41b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final Command COMMAND_WITH_ARGS = new Command(COMMAND_NAME, EMPTY);
42b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final int REPLY_CODE1 = 777;
43b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final int REPLY_CODE2 = 888;
44b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final int REPLY_CODE3 = 999;
45b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final String REPLY_TEXT1 = "reply1 ... abcdef";
46b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final String REPLY_TEXT2 = "abc {0} def";
47b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final String REPLY_TEXT2_FORMATTED = "abc 123 def";
48b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final String OVERRIDE_REPLY_TEXT = "overridden reply ... abcdef";
49b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final String MESSAGE_KEY = "key.123";
50b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private static final String MESSAGE_TEXT = "message.123";
51b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
52b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private AbstractTrackingCommandHandler commandHandler;
53b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    private Session session;
54b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
55b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
56b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Test the handleCommand(Command,Session) method
57b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
58b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    public void testHandleCommand() throws Exception {
59b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        assertEquals("before", 0, commandHandler.numberOfInvocations());
60b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.handleCommand(COMMAND, session);
61b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        assertEquals("after", 1, commandHandler.numberOfInvocations());
62b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        assertTrue("locked", commandHandler.getInvocation(0).isLocked());
63b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
64b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
65b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
66b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Test the handleCommand(Command,Session) method, passing in a null Command
67b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
68b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    public void testHandleCommand_NullCommand() throws Exception {
69b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        try {
70b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            commandHandler.handleCommand(null, session);
71b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            fail("Expected AssertFailedException");
72b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        }
73b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        catch (AssertFailedException expected) {
74b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            LOG.info("Expected: " + expected);
75b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        }
76b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
77b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
78b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
79b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Test the handleCommand(Command,Session) method, passing in a null Session
80b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
81b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    public void testHandleCommand_NullSession() throws Exception {
82b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        try {
83b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            commandHandler.handleCommand(COMMAND, null);
84b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            fail("Expected AssertFailedException");
85b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        }
86b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        catch (AssertFailedException expected) {
87b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            LOG.info("Expected: " + expected);
88b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        }
89b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
90b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
91b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
92b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Test the numberOfInvocations(), addInvocationRecord() and clearInvocationRecord() methods
93b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
94b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    public void testInvocationHistory() throws Exception {
95b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        control(session).expectAndDefaultReturn(session.getClientHost(), DEFAULT_HOST);
96b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        replay(session);
97b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
98b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        assertEquals("none", 0, commandHandler.numberOfInvocations());
99b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.handleCommand(COMMAND, session);
100b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        assertEquals("1", 1, commandHandler.numberOfInvocations());
101b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.handleCommand(COMMAND, session);
102b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        assertEquals("2", 2, commandHandler.numberOfInvocations());
103b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.clearInvocations();
104b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        assertEquals("cleared", 0, commandHandler.numberOfInvocations());
105b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
106b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
107b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
108b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Test the getInvocation() method
109b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     *
110b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * @throws Exception
111b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
112b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    public void testGetInvocation() throws Exception {
113b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        control(session).expectAndDefaultReturn(session.getClientHost(), DEFAULT_HOST);
114b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        replay(session);
115b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
116b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.handleCommand(COMMAND, session);
117b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.handleCommand(COMMAND_WITH_ARGS, session);
118b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        assertSame("1", COMMAND, commandHandler.getInvocation(0).getCommand());
119b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        assertSame("2", COMMAND_WITH_ARGS, commandHandler.getInvocation(1).getCommand());
120b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
121b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
122b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
123b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Test the getInvocation() method, passing in an invalid index
124b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
125b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    public void testGetInvocation_IndexOutOfBounds() throws Exception {
126b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.handleCommand(COMMAND, session);
127b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        try {
128b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            commandHandler.getInvocation(2);
129b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            fail("Expected IndexOutOfBoundsException");
130b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        }
131b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        catch (IndexOutOfBoundsException expected) {
132b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            LOG.info("Expected: " + expected);
133b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        }
134b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
135b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
136b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
137b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Test the sendReply() method, when no message arguments are specified
138b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
139b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    public void testSendReply() {
140b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        session.sendReply(REPLY_CODE1, REPLY_TEXT1);
141b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        session.sendReply(REPLY_CODE1, MESSAGE_TEXT);
142b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        session.sendReply(REPLY_CODE1, OVERRIDE_REPLY_TEXT);
143b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        session.sendReply(REPLY_CODE3, null);
144b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        replay(session);
145b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
146b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.sendReply(session, REPLY_CODE1, null, null, null);
147b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.sendReply(session, REPLY_CODE1, MESSAGE_KEY, null, null);
148b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.sendReply(session, REPLY_CODE1, MESSAGE_KEY, OVERRIDE_REPLY_TEXT, null);
149b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.sendReply(session, REPLY_CODE3, null, null, null);
150b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
151b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        verify(session);
152b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
153b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
154b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
155b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Test the sendReply() method, passing in message arguments
156b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
157b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    public void testSendReply_WithMessageArguments() {
158b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        session.sendReply(REPLY_CODE1, REPLY_TEXT2_FORMATTED);
159b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        replay(session);
160b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
161b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.sendReply(session, REPLY_CODE1, null, REPLY_TEXT2, ARGS);
162b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
163b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        verify(session);
164b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
165b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
166b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
167b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Test the sendReply() method, passing in a null Session
168b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
169b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    public void testSendReply_NullSession() {
170b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        try {
171b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            commandHandler.sendReply(null, REPLY_CODE1, REPLY_TEXT1, null, null);
172b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            fail("Expected AssertFailedException");
173b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        }
174b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        catch (AssertFailedException expected) {
175b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            LOG.info("Expected: " + expected);
176b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        }
177b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
178b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
179b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
180b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Test the sendReply() method, passing in an invalid replyCode
181b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
182b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    public void testSendReply_InvalidReplyCode() {
183b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        try {
184b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            commandHandler.sendReply(session, 0, REPLY_TEXT1, null, null);
185b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            fail("Expected AssertFailedException");
186b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        }
187b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        catch (AssertFailedException expected) {
188b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            LOG.info("Expected: " + expected);
189b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        }
190b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
191b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
192b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    //-------------------------------------------------------------------------
193b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    // Test setup
194b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    //-------------------------------------------------------------------------
195b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
196b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    /**
197b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * Perform initialization before each test
198b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     *
199b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     * @see org.mockftpserver.test.AbstractTest#setUp()
200b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair     */
201b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    protected void setUp() throws Exception {
202b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        super.setUp();
203b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        session = (Session) createMock(Session.class);
204b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        control(session).setDefaultMatcher(MockControl.ARRAY_MATCHER);
205b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler = new AbstractTrackingCommandHandler() {
206b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            public void handleCommand(Command command, Session session, InvocationRecord invocationRecord) throws Exception {
207b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            }
208b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        };
209b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        ResourceBundle replyTextBundle = new ListResourceBundle() {
210b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            protected Object[][] getContents() {
211b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair                return new Object[][]{
212b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair                        {Integer.toString(REPLY_CODE1), REPLY_TEXT1},
213b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair                        {Integer.toString(REPLY_CODE2), REPLY_TEXT2},
214b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair                        {MESSAGE_KEY, MESSAGE_TEXT}
215b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair                };
216b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair            }
217b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        };
218b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair        commandHandler.setReplyTextBundle(replyTextBundle);
219b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair    }
220b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair
221b2f4a2dfc590c250e42b21eb40d9539ac135b495chrismair}
222