15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/scoped_temp_dir.h"
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
8868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/sessions/session_backend.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)typedef std::vector<SessionCommand*> SessionCommands;
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct TestData {
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SessionCommand::id_type command_id;
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string data;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SessionCommand* CreateCommandFromData(const TestData& data) {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SessionCommand* command =
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new SessionCommand(
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          data.command_id,
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          static_cast<SessionCommand::size_type>(data.data.size()));
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!data.data.empty())
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    memcpy(command->contents(), data.data.c_str(), data.data.size());
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return command;
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class SessionBackendTest : public testing::Test {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected:
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void SetUp() {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    path_ = temp_dir_.path().Append(FILE_PATH_LITERAL("SessionTestDirs"));
38a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::CreateDirectory(path_);
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AssertCommandEqualsData(const TestData& data, SessionCommand* command) {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_EQ(data.command_id, command->id());
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_EQ(data.data.size(), command->size());
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EXPECT_TRUE(
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        memcmp(command->contents(), data.data.c_str(), command->size()) == 0);
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Path used in testing.
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::FilePath path_;
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::ScopedTempDir temp_dir_;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(SessionBackendTest, SimpleReadWrite) {
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<SessionBackend> backend(
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new SessionBackend(BaseSessionService::SESSION_RESTORE, path_));
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct TestData data = { 1,  "a" };
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SessionCommand*> commands;
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.push_back(CreateCommandFromData(data));
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->AppendCommands(new SessionCommands(commands), false);
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.clear();
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Read it back in.
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend = NULL;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend = new SessionBackend(BaseSessionService::SESSION_RESTORE, path_);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->ReadLastSessionCommandsImpl(&commands);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(1U, commands.size());
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AssertCommandEqualsData(data, commands[0]);
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  STLDeleteElements(&commands);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend = NULL;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend = new SessionBackend(BaseSessionService::SESSION_RESTORE, path_);
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->ReadLastSessionCommandsImpl(&commands);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(0U, commands.size());
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Make sure we can delete.
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->DeleteLastSession();
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->ReadLastSessionCommandsImpl(&commands);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(0U, commands.size());
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(SessionBackendTest, RandomData) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct TestData data[] = {
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 1,  "a" },
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 2,  "ab" },
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 3,  "abc" },
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 4,  "abcd" },
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 5,  "abcde" },
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 6,  "abcdef" },
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 7,  "abcdefg" },
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 8,  "abcdefgh" },
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 9,  "abcdefghi" },
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 10, "abcdefghij" },
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 11, "abcdefghijk" },
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 12, "abcdefghijkl" },
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 13, "abcdefghijklm" },
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < arraysize(data); ++i) {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_refptr<SessionBackend> backend(
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new SessionBackend(BaseSessionService::SESSION_RESTORE, path_));
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<SessionCommand*> commands;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (i != 0) {
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Read previous data.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      backend->ReadLastSessionCommandsImpl(&commands);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ASSERT_EQ(i, commands.size());
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (std::vector<SessionCommand*>::iterator j = commands.begin();
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           j != commands.end(); ++j) {
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        AssertCommandEqualsData(data[j - commands.begin()], *j);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      backend->AppendCommands(new SessionCommands(commands), false);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      commands.clear();
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    commands.push_back(CreateCommandFromData(data[i]));
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    backend->AppendCommands(new SessionCommands(commands), false);
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(SessionBackendTest, BigData) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct TestData data[] = {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 1,  "a" },
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { 2,  "ab" },
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<SessionBackend> backend(
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new SessionBackend(BaseSessionService::SESSION_RESTORE, path_));
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SessionCommand*> commands;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.push_back(CreateCommandFromData(data[0]));
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SessionCommand::size_type big_size =
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SessionBackend::kFileReadBufferSize + 100;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SessionCommand::id_type big_id = 50;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SessionCommand* big_command = new SessionCommand(big_id, big_size);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reinterpret_cast<char*>(big_command->contents())[0] = 'a';
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reinterpret_cast<char*>(big_command->contents())[big_size - 1] = 'z';
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.push_back(big_command);
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.push_back(CreateCommandFromData(data[1]));
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->AppendCommands(new SessionCommands(commands), false);
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.clear();
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend = NULL;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend = new SessionBackend(BaseSessionService::SESSION_RESTORE, path_);
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.clear();
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->ReadLastSessionCommandsImpl(&commands);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(3U, commands.size());
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AssertCommandEqualsData(data[0], commands[0]);
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AssertCommandEqualsData(data[1], commands[2]);
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ(big_id, commands[1]->id());
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(big_size, commands[1]->size());
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ('a', reinterpret_cast<char*>(commands[1]->contents())[0]);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_EQ('z',
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            reinterpret_cast<char*>(commands[1]->contents())[big_size - 1]);
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  STLDeleteElements(&commands);
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)TEST_F(SessionBackendTest, EmptyCommand) {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TestData empty_command;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  empty_command.command_id = 1;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<SessionBackend> backend(
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new SessionBackend(BaseSessionService::SESSION_RESTORE, path_));
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SessionCommand*>* empty_commands =
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new std::vector<SessionCommand*>();
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  empty_commands->push_back(CreateCommandFromData(empty_command));
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->AppendCommands(empty_commands, true);
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->MoveCurrentSessionToLastSession();
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SessionCommand*> commands;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->ReadLastSessionCommandsImpl(&commands);
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(1U, commands.size());
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AssertCommandEqualsData(empty_command, commands[0]);
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  STLDeleteElements(&commands);
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Writes a command, appends another command with reset to true, then reads
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// making sure we only get back the second command.
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST_F(SessionBackendTest, Truncate) {
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<SessionBackend> backend(
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new SessionBackend(BaseSessionService::SESSION_RESTORE, path_));
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct TestData first_data = { 1,  "a" };
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<SessionCommand*> commands;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.push_back(CreateCommandFromData(first_data));
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->AppendCommands(new SessionCommands(commands), false);
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.clear();
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Write another command, this time resetting the file when appending.
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct TestData second_data = { 2,  "b" };
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.push_back(CreateCommandFromData(second_data));
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->AppendCommands(new SessionCommands(commands), true);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  commands.clear();
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Read it back in.
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend = NULL;
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend = new SessionBackend(BaseSessionService::SESSION_RESTORE, path_);
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  backend->ReadLastSessionCommandsImpl(&commands);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // And make sure we get back the expected data.
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ASSERT_EQ(1U, commands.size());
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AssertCommandEqualsData(second_data, commands[0]);
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  STLDeleteElements(&commands);
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
204