Ticket #1396: track1396.patch

File track1396.patch, 50.1 KB (added by Teiresias, 10 years ago)

Proposal for utf8-support unit-tests

  • binaries/data/mods/_test.sim/simulation/components/test-bom-none.js

     
     1/* A simple JavaScript file in UTF-8 without byte order mark
     2   at the start of the file, to ensure such scripts are handled
     3   properly. */
     4Engine.Note = "Script without BOM was accepted";
  • binaries/data/mods/_test.sim/simulation/components/test-bom-utf8.js

     
     1/* A simple JavaScript file in UTF-8 with byte order mark/ at the start
     2   of the file (probably not visible in a regular editor), to ensure such
     3   scripts are handled properly.*/
     4
     5Engine.Note = "UTF8-BOM was accepted"; /* ID to ensure
     6    the function was really evaluated ☺ */
  • source/gui/CGUI.h

     
    421421    void Xeromyces_ReadRootSetup(XMBElement Element, CXeromyces* pFile);
    422422
    423423    // Read Subs
    424 
     424protected: // Members protected so unit tests can access them via inheritance
     425   
    425426    /**
    426427     * Notice! Recursive function!
    427428     *
  • source/gui/tests/test_CGUI.h

     
     1/* Copyright (C) 2014 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#include "gui/CGUI.h"
     19#include "gui/IGUIObject.h"
     20#include "lib/utf8.h"
     21#include "lib/file/file.h"
     22#include "lib/file/vfs/vfs.h"
     23#include "scriptinterface/ScriptInterface.h"
     24#include "ps/CLogger.h"
     25#include "ps/Filesystem.h"
     26
     27/**
     28 * Helper namespace to keep utility classes for testing of CGUI, namely
     29 * the VFS fakes (prevent collision with other tests)
     30 */
     31namespace test_CGUI
     32{
     33    /**
     34     * Lightweight testing implementation of a virtual file system.
     35     *
     36     * While the TestSimulation2 class already has an associated mod (_test.sim),
     37     * testing of the file loader routines via a stub has several advantages:
     38     *  1.  Some routines, such as GetRMSData(), scan a whole directory whose path is
     39     *      hard-coded in the testee. Using a real VFS might cause undesired interferences
     40     *      as more tests are added for a single routine.
     41     *  2.  A pure in-memory VFS is much faster.
     42     *  3.  For UTF-8 tests, control characters (such as a leading BOM) may be expressed
     43     *      directly in the C code, while they might be invisible in some editors when
     44     *      put in real files.
     45     */
     46    struct StubVFS : public IVFS {
     47
     48        /**
     49         * Simulates a file.
     50         */
     51        class StubFile
     52        {
     53        public:
     54
     55            /**
     56             * Initializes a new stub file with fixed content.
     57             * @param filename Name of the file.
     58             * @param dataLength Size of the data held by the fake file.
     59             * @param data Data found in the associated file.
     60             */
     61            StubFile(const char filename[], size_t dataLength, const u8 data[]) :
     62                m_FileName(filename),
     63                m_FileSize(dataLength)
     64            {
     65                u8 *dataCopy = new u8[dataLength];
     66                memcpy(dataCopy, data, dataLength);
     67                this->m_FileContent = shared_ptr<u8>(dataCopy);
     68            }
     69
     70            /**
     71             * Provides the data size of the file.
     72             * @return An info record with file size and a fake date.
     73             */
     74            CFileInfo getInfo() const
     75            {
     76                return CFileInfo(this->getName(), this->m_FileSize, 0);
     77            }
     78
     79            /**
     80             * Retrieves the name of the file.
     81             * @return The filename passed into the constructor.
     82             */
     83            OsPath getName() const
     84            {
     85                return OsPath(this->m_FileName);
     86            }
     87
     88            /**
     89             * Retrieves the data of this file.
     90             * @return A copy of the data passed to the constructor.
     91             */
     92            shared_ptr<u8> getContent() const
     93            {
     94                return this->m_FileContent;
     95            }
     96
     97        private:
     98
     99            /**
     100             * Name of this file.
     101             */
     102            const char *m_FileName;
     103
     104            /**
     105             * Data found in the file represented by this object
     106             */
     107            shared_ptr<u8> m_FileContent;
     108
     109            /**
     110             * Data size of 'm_FileContent'
     111             */
     112            const size_t m_FileSize;
     113        };
     114
     115        /**
     116         * Creates a stub file system which simulates a fixed set of files.
     117         * @param numberOfFiles Number of valid entries in 'files'.
     118         * @param files The files which are provided (no real support for
     119         *              directories, all files are returned on each call to
     120         *              'GetDirectoryEntries').
     121         */
     122        StubVFS(size_t numberOfFiles, const StubFile files[]) :
     123            m_NumberOfFiles(numberOfFiles)
     124        {
     125            this->m_Files = files;
     126        }
     127
     128        virtual Status Mount(const VfsPath&, const OsPath&, size_t, size_t)
     129        {
     130            throw "Not yet implemented";
     131        }
     132
     133        virtual Status GetFileInfo(const VfsPath &path, CFileInfo *pInfo) const
     134        {
     135            for (size_t i = 0; i < this->m_NumberOfFiles; i++)
     136            {
     137                if (this->m_Files[i].getName() == path.Filename())
     138                {
     139                    if (pInfo)
     140                    {
     141                        *pInfo = this->m_Files[i].getInfo();
     142                    }
     143                    return INFO::OK;
     144                }
     145            }
     146            return ERR::FILE_NOT_FOUND;
     147        }
     148
     149        virtual Status GetFilePriority(const VfsPath &path, size_t *ppriority) const
     150        {
     151            for (size_t i = 0; i < this->m_NumberOfFiles; i++)
     152            {
     153                if (this->m_Files[i].getName() == path.Filename())
     154                {
     155                    *ppriority = 1;
     156                    return INFO::OK;
     157                }
     158            }
     159            return ERR::FILE_NOT_FOUND;
     160        }
     161
     162        /**
     163         * Retrieves a listing of all files held by this file system.
     164         * @param infos Receives additional information about a file.
     165         * @param names Receives the names of all files in the file system.
     166         * @return OK.
     167         */
     168        virtual Status GetDirectoryEntries(const VfsPath&, CFileInfos* infos, DirectoryNames* names) const
     169        {
     170            for (size_t i = 0; i < this->m_NumberOfFiles; i++)
     171            {
     172                if (infos)
     173                {
     174                    infos->push_back(this->m_Files[i].getInfo());
     175                }
     176                if (names)
     177                {
     178                    names->push_back(this->m_Files[i].getName());
     179                }
     180            }
     181            return INFO::OK;
     182        }
     183
     184        virtual Status CreateFile(const VfsPath &, const shared_ptr<u8>&, size_t)
     185        {
     186            // Currently, test cases do not involve writing files back to disk;
     187            // this function is called by Xeromyces in an attempt to cache an XML
     188            // document in binary mode
     189            return INFO::OK;
     190        }
     191
     192        virtual Status ReplaceFile(const VfsPath&, const shared_ptr<u8>&, size_t)
     193        {
     194            throw "Not yet implemented";
     195        }
     196
     197        /**
     198         * Read an entire file into memory.
     199         *
     200         * @param path Selects the fake file whose content is requested.
     201         * @param fileContents receives a smart pointer to the contents.
     202         *        CAVEAT: this will be taken from the file cache if the VFS was
     203         *        created with cacheSize != 0 and size < cacheSize. There is no
     204         *        provision for Copy-on-Write, which means that such buffers
     205         *        must not be modified (this is enforced via mprotect).
     206         * @param size receives the size [bytes] of the file contents.
     207         * @return OK, if a fake file with given path was selected, or
     208         *         OpenFailed in case the file does not exist.
     209         **/
     210        virtual Status LoadFile(const VfsPath& path, shared_ptr<u8>& fileContents, size_t& size)
     211        {
     212            for (size_t i = 0; i < this->m_NumberOfFiles; i++)
     213            {
     214                if (this->m_Files[i].getName() == path.Filename())
     215                {
     216                    fileContents = this->m_Files[i].getContent();
     217                    size = this->m_Files[i].getInfo().Size();
     218                    return INFO::OK;
     219                }
     220            }
     221            return CODE__PSRETURN_Scripting_LoadFile_OpenFailed;
     222        }
     223
     224        virtual std::wstring TextRepresentation() const
     225        {
     226            throw "Not yet implemented";
     227        }
     228
     229        virtual Status GetRealPath(const VfsPath &virtualPath, OsPath &realPath)
     230        {
     231            realPath = virtualPath;
     232            return INFO::OK;
     233        }
     234
     235        virtual Status GetDirectoryRealPath(const VfsPath&, OsPath&)
     236        {
     237            throw "Not yet implemented";
     238        }
     239
     240        virtual Status GetVirtualPath(const OsPath&, VfsPath&)
     241        {
     242            throw "Not yet implemented";
     243        }
     244
     245        /**
     246         * remove file from the virtual directory listing and evict its
     247         * data from the cache.
     248         **/
     249        virtual Status RemoveFile(const VfsPath&)
     250        {
     251            throw "Not yet implemented";
     252        }
     253
     254        virtual Status RepopulateDirectory(const VfsPath&)
     255        {
     256            throw "Not yet implemented";
     257        }
     258
     259        virtual void Clear()
     260        {
     261            throw "Not yet implemented";
     262        }
     263
     264    private:
     265
     266        /**
     267         * Number of valid elements in 'm_Files'.
     268         */
     269        const size_t m_NumberOfFiles;
     270
     271        /**
     272         * The files found in this file system.
     273         */
     274        const StubFile *m_Files;
     275    };
     276
     277    /**
     278     * Helper to collect the actual elements loaded from Xeromyces
     279     */
     280    class MockGUIObject : public IGUIObject
     281    {
     282    public:
     283
     284        virtual void Draw(void)
     285        {
     286            // No need to draw anything, used only for testing
     287        }
     288
     289        /**
     290         * Retrieves the first child from the internal list of children.
     291         * @return A reference to the first child, NULL if there are no children.
     292         */
     293        IGUIObject *GetFirstChild()
     294        {
     295            return this->m_Children.size() > 0 ? this->m_Children[0] : NULL;
     296        }
     297
     298        /**
     299         * Helper member for the BOM tests to run the JavaScript handlers registered
     300         * by the Xeromyces loader
     301         */
     302        virtual void HandleMessage(SGUIMessage& UNUSED(Message))
     303        {
     304            this->ScriptEvent("validatetest");
     305        }
     306
     307    };
     308}
     309
     310class TestCGUI : public CxxTest::TestSuite
     311{
     312public:
     313
     314    class CGUI_TestHook : public CGUI
     315    {
     316    public:
     317        CGUI_TestHook(const shared_ptr<ScriptRuntime>& runtime) : CGUI(runtime)
     318        {
     319        }
     320
     321        void Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObject *pParent, const std::vector<std::pair<CStr, CStr> >& NameSubst, boost::unordered_set<VfsPath>& Paths)
     322        {
     323            CGUI::Xeromyces_ReadObject(Element, pFile, pParent, NameSubst, Paths);
     324        }
     325
     326    };
     327
     328
     329    void setUp()
     330    {
     331        CXeromyces::Startup();
     332    }
     333
     334    void tearDown()
     335    {
     336        CXeromyces::Terminate();
     337    }   
     338
     339    static IGUIObject *CreateMockObject()
     340    {
     341        return new test_CGUI::MockGUIObject();
     342    }
     343
     344    /**
     345     * An action handler (external JavaScript file) is loaded when stored as a
     346     * raw ASCII file.
     347     */
     348    void test_Xeromyces_ReadObject_RawASCIISupportedAsActionScript()
     349    {
     350        const u8 xmlData[] = "\
     351<object type=\"testobj\">\
     352    <action file=\"test.js\" on=\"validatetest\"/>\
     353</object>";
     354        const u8 jsData[] = "warn(\"Raw ASCII accepted!\");";
     355
     356        test_CGUI::StubVFS::StubFile testFiles[] =
     357        {
     358            test_CGUI::StubVFS::StubFile("test.xml", sizeof(xmlData)/sizeof(xmlData[0]) - 1, xmlData),
     359            test_CGUI::StubVFS::StubFile("test.js", sizeof(jsData)/sizeof(jsData[0]) - 1, jsData)
     360        };
     361        PIVFS originalVFS(g_VFS);
     362        g_VFS = PIVFS(new test_CGUI::StubVFS(2, testFiles));
     363
     364        CXeromyces testObjDescription;
     365        PSRETURN loadingResult = testObjDescription.Load(g_VFS, "test.xml");
     366
     367        TS_ASSERT_EQUALS(loadingResult, INFO::OK);
     368
     369        XMBElement rootNode = testObjDescription.GetRoot();
     370
     371        CGUI_TestHook testee(ScriptInterface::CreateRuntime());
     372        std::vector<std::pair<CStr8, CStr8> > nameSubst;
     373        boost::unordered_set<VfsPath> vfsPaths;
     374        test_CGUI::MockGUIObject mockParent;
     375
     376        testee.AddObjectType("testobj", &CreateMockObject);
     377
     378        testee.Xeromyces_ReadObject(
     379            rootNode,
     380            &testObjDescription,
     381            &mockParent,
     382            nameSubst,
     383            vfsPaths);
     384
     385        IGUIObject *pGeneratedChild = mockParent.GetFirstChild();
     386
     387        // If the test setup above is successfull, pGeneratedChild
     388        // is actually a MockGUIObject and its HandleMessage implementation
     389        // will run the JavaScript code.
     390        TS_ASSERT(NULL != pGeneratedChild);
     391
     392        TestLogger log;
     393
     394        SGUIMessage message(GUIM_MOUSE_OVER);
     395        pGeneratedChild->HandleMessage(message);
     396
     397        TS_ASSERT_WSTR_CONTAINS(log.GetOutput(), L"Raw ASCII accepted!");
     398
     399        g_VFS = originalVFS;
     400    }
     401
     402    /**
     403     * An action handler (external JavaScript file) is loaded when stored with
     404     * an UTF-8 byte-order-mark at the start of the script code, leaving valid
     405     * JavaScript.
     406     */
     407    void test_Xeromyces_ReadObject_UTF8SupportedAsActionScript()
     408    {
     409        const u8 xmlData[] = "\
     410<object type=\"testobj\">\
     411    <action file=\"test.js\" on=\"validatetest\"/>\
     412</object>";
     413        const u8 jsData[] = "\xEF\xBB\xBFwarn(\"UTF8-BOM accepted!\");";
     414
     415        test_CGUI::StubVFS::StubFile testFiles[] =
     416        {
     417            test_CGUI::StubVFS::StubFile("test.xml", sizeof(xmlData)/sizeof(xmlData[0]) - 1, xmlData),
     418            test_CGUI::StubVFS::StubFile("test.js", sizeof(jsData)/sizeof(jsData[0]) - 1, jsData)
     419        };
     420        PIVFS originalVFS(g_VFS);
     421        g_VFS = PIVFS(new test_CGUI::StubVFS(2, testFiles));
     422
     423        CXeromyces testObjDescription;
     424        PSRETURN loadingResult = testObjDescription.Load(g_VFS, "test.xml");
     425
     426        TS_ASSERT_EQUALS(loadingResult, INFO::OK);
     427
     428        XMBElement rootNode = testObjDescription.GetRoot();
     429
     430        CGUI_TestHook testee(ScriptInterface::CreateRuntime());
     431        std::vector<std::pair<CStr8, CStr8> > nameSubst;
     432        boost::unordered_set<VfsPath> vfsPaths;
     433        test_CGUI::MockGUIObject mockParent;
     434
     435        testee.AddObjectType("testobj", &CreateMockObject);
     436
     437        testee.Xeromyces_ReadObject(
     438            rootNode,
     439            &testObjDescription,
     440            &mockParent,
     441            nameSubst,
     442            vfsPaths);
     443
     444        IGUIObject *pGeneratedChild = mockParent.GetFirstChild();
     445
     446        // If the test setup above is successfull, pGeneratedChild
     447        // is actually a MockGUIObject and its HandleMessage implementation
     448        // will run the JavaScript code
     449        TS_ASSERT(NULL != pGeneratedChild);
     450
     451        TestLogger log;
     452
     453        SGUIMessage message(GUIM_MOUSE_OVER);
     454        pGeneratedChild->HandleMessage(message);
     455
     456        TS_ASSERT_WSTR_CONTAINS(log.GetOutput(), L"UTF8-BOM accepted!");
     457
     458        g_VFS = originalVFS;
     459    }
     460
     461};
  • source/ps/GameSetup/HWDetect.cpp

     
    186186    g_RenderPath = renderpath;
    187187}
    188188
    189 void RunHardwareDetection()
     189/**
     190 * Loads and initializes the JavaScript library used for hardware
     191 * detection.
     192 * @param scriptInterface The runtime used for the JS part of the HW detection.
     193 * @return true if and only if the script was loaded successfully.
     194 */
     195bool LoadHardwareDetectionLibrary(ScriptInterface &scriptInterface)
    190196{
    191     TIMER(L"RunHardwareDetection");
    192 
    193     ScriptInterface scriptInterface("Engine", "HWDetect", g_ScriptRuntime);
    194 
    195197    scriptInterface.RegisterFunction<void, bool, &SetDisableAudio>("SetDisableAudio");
    196198    scriptInterface.RegisterFunction<void, bool, &SetDisableS3TC>("SetDisableS3TC");
    197199    scriptInterface.RegisterFunction<void, bool, &SetDisableShadows>("SetDisableShadows");
     
    208210    if (file.Load(g_VFS, scriptName) != PSRETURN_OK)
    209211    {
    210212        LOGERROR(L"Failed to load hardware detection script");
    211         return;
     213        return false;
    212214    }
    213215
    214216    std::string code = file.DecodeUTF8(); // assume it's UTF-8
    215     scriptInterface.LoadScript(scriptName, code);
     217    return scriptInterface.LoadScript(scriptName, code);
     218}
    216219
     220void RunHardwareDetection()
     221{
     222    TIMER(L"RunHardwareDetection");
     223
     224    ScriptInterface scriptInterface("Engine", "HWDetect", g_ScriptRuntime);
     225
     226    if (LoadHardwareDetectionLibrary(scriptInterface))
     227    {
     228        return;
     229    }
     230
    217231    // Collect all the settings we'll pass to the script:
    218232    // (We'll use this same data for the opt-in online reporting system, so it
    219233    // includes some fields that aren't directly useful for the hwdetect script)
  • source/ps/GameSetup/tests/test_HWDetect.h

     
     1/* Copyright (C) 2010 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#include "lib/file/vfs/vfs.h"
     19#include "scriptinterface/ScriptInterface.h"
     20#include "ps/Filesystem.h"
     21#include "ps/GameSetup/Config.h"
     22#include "ps/GameSetup/HWDetect.h"
     23
     24/**
     25 * Holds utility classes for testing of the HWDetection routines
     26 *
     27 * Multiple classes use stub implementation of the IVFS
     28 * interface; to prevent name collisions among them, the stub is
     29 * encapsulated in its own namespace.
     30 */
     31namespace test_HWDetect
     32{
     33
     34    /**
     35     * Lightweight testing implementation of a virtual file system.
     36     */
     37    struct StubVFS : public IVFS {
     38
     39        /**
     40         * Creates a stub file system which returns a fixed file content for any file.
     41         * @param dataLength Number of valid consecutive data bytes in 'fileContent'.
     42         * @param fileContent "Data" of all files in the stub file system.
     43         */
     44        StubVFS(size_t dataLength, const u8 fileContent[]) : m_FileSize(dataLength)
     45        {
     46            // As the IVFS is defined to return a smart pointer to the data -
     47            // see LoadFile() - we have to create a copy of the supplied primitive
     48            // memory block, getting rid of the const at the same time.
     49
     50            u8 *dataCopy = new u8[dataLength];
     51            memcpy(dataCopy, fileContent, dataLength);
     52            this->m_FileContent = shared_ptr<u8>(dataCopy);
     53        }
     54
     55        virtual Status Mount(const VfsPath&, const OsPath&, size_t, size_t)
     56        {
     57            throw "Not yet implemented";
     58        }
     59
     60        virtual Status GetFileInfo(const VfsPath&, CFileInfo*) const
     61        {
     62            throw "Not yet implemented";
     63        }
     64
     65        virtual Status GetFilePriority(const VfsPath&, size_t*) const
     66        {
     67            throw "Not yet implemented";
     68        }
     69
     70        virtual Status GetDirectoryEntries(const VfsPath&, CFileInfos*, DirectoryNames*) const
     71        {
     72            throw "Not yet implemented";
     73        }
     74
     75        virtual Status CreateFile(const VfsPath&, const shared_ptr<u8>&, size_t)
     76        {
     77            throw "Not yet implemented";
     78        }
     79
     80        virtual Status ReplaceFile(const VfsPath&, const shared_ptr<u8>&, size_t)
     81        {
     82            throw "Not yet implemented";
     83        }
     84
     85        /**
     86         * Read an entire file into memory.
     87         *
     88         * @param fileContents receives a smart pointer to the contents.
     89         *        CAVEAT: this will be taken from the file cache if the VFS was
     90         *        created with cacheSize != 0 and size < cacheSize. There is no
     91         *        provision for Copy-on-Write, which means that such buffers
     92         *        must not be modified (this is enforced via mprotect).
     93         * @param size receives the size [bytes] of the file contents.
     94         * @return Status.
     95         **/
     96        virtual Status LoadFile(const VfsPath&, shared_ptr<u8>& fileContents, size_t& size)
     97        {
     98            fileContents = this->m_FileContent;
     99            size = this->m_FileSize;
     100            return INFO::OK;
     101        }
     102
     103        virtual std::wstring TextRepresentation() const
     104        {
     105            throw "Not yet implemented";
     106        }
     107
     108        virtual Status GetRealPath(const VfsPath&, OsPath&)
     109        {
     110            throw "Not yet implemented";
     111        }
     112
     113        virtual Status GetDirectoryRealPath(const VfsPath&, OsPath&)
     114        {
     115            throw "Not yet implemented";
     116        }
     117
     118        virtual Status GetVirtualPath(const OsPath&, VfsPath&)
     119        {
     120            throw "Not yet implemented";
     121        }
     122
     123        virtual Status RemoveFile(const VfsPath&)
     124        {
     125            throw "Not yet implemented";
     126        }
     127
     128        virtual Status RepopulateDirectory(const VfsPath&)
     129        {
     130            throw "Not yet implemented";
     131        }
     132
     133        virtual void Clear()
     134        {
     135            throw "Not yet implemented";
     136        }
     137
     138    private:
     139
     140        /**
     141         * Data found in all "files" of this file system.
     142         */
     143        shared_ptr<u8> m_FileContent;
     144
     145        /**
     146         * Number of valid elements in 'm_FileContent'
     147         */
     148        const size_t m_FileSize;
     149    };
     150
     151}
     152
     153// In theory, the loader function should not be visible outside HWDetect.cpp,
     154// but since that file is procedural, the usual trick of declaring friend with
     155// the testee cannot be used to gain access to the function tested. As a
     156// compromise, the prototype is declared in the test only.
     157extern bool LoadHardwareDetectionLibrary(ScriptInterface &scriptInterface);
     158
     159class TestHWDetect : public CxxTest::TestSuite
     160{
     161public:
     162
     163    /**
     164     * The hardware detection JavaScript used by the HWDetect sub-module is accepted
     165     * when it contains a lead-in byte order mark.
     166     */
     167    void test_LoadHWDetection_accepts_BOM()
     168    {
     169        static const u8 detectionScript[] = "\xEF\xBB\xBF Engine.SetRenderPath(\"BOM accepted\");";
     170        PIVFS originalVFS(g_VFS);
     171        CStr originalRenderPath(g_RenderPath);
     172
     173        g_VFS =
     174            PIVFS(new test_HWDetect::StubVFS(
     175                sizeof(detectionScript)/sizeof(detectionScript[0]) - 1, // -1 as JS does not accept \0
     176                detectionScript));
     177
     178        shared_ptr<ScriptRuntime> runtime = ScriptInterface::CreateRuntime();
     179        ScriptInterface scriptInterface("Engine", "HWDetect", runtime);
     180
     181        LoadHardwareDetectionLibrary(scriptInterface);
     182
     183        TS_ASSERT_EQUALS(g_RenderPath, "BOM accepted");
     184
     185        g_VFS = originalVFS;
     186        g_RenderPath = originalRenderPath;
     187    }
     188
     189    /**
     190     * The hardware detection JavaScript used by the HWDetect.cpp sub-module is accepted
     191     * when it does not start with a byte-order-mark (e.g. a plain ASCII file)
     192     */
     193    void test_LoadHWDetection_accepts_without_BOM()
     194    {
     195        static const u8 detectionScript[] = "Engine.SetRenderPath(\"ASCII accepted\");";
     196        PIVFS originalVFS(g_VFS);
     197        CStr originalRenderPath(g_RenderPath);
     198
     199        g_VFS =
     200            PIVFS(new test_HWDetect::StubVFS(
     201                sizeof(detectionScript)/sizeof(detectionScript[0]) - 1, // -1 as JS does not accept \0
     202                detectionScript));
     203
     204        shared_ptr<ScriptRuntime> runtime = ScriptInterface::CreateRuntime();
     205        ScriptInterface scriptInterface("Engine", "HWDetect", runtime);
     206
     207        LoadHardwareDetectionLibrary(scriptInterface);
     208
     209        TS_ASSERT_EQUALS(g_RenderPath, "ASCII accepted");
     210
     211        g_VFS = originalVFS;
     212        g_RenderPath = originalRenderPath;
     213    }
     214
     215};
  • source/ps/tests/test_CVFSFile.h

     
     1/* Copyright (C) 2012 Wildfire Games.
     2 * This file is part of 0 A.D.
     3 *
     4 * 0 A.D. is free software: you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License as published by
     6 * the Free Software Foundation, either version 2 of the License, or
     7 * (at your option) any later version.
     8 *
     9 * 0 A.D. is distributed in the hope that it will be useful,
     10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 * GNU General Public License for more details.
     13 *
     14 * You should have received a copy of the GNU General Public License
     15 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
     16 */
     17
     18#include "precompiled.h"
     19#include "ps/Filesystem.h"
     20
     21/**
     22 * Holds utility classes for testing of the CVFSFile class.
     23 *
     24 * The unit-tests of the CVFSFile use a stub implementation of the IVFS
     25 * interface; to prevent name collisions with other tests, the stub is
     26 * encapsulated in its own namespace.
     27 */
     28namespace test_CVFSFile
     29{
     30
     31    /**
     32     * Lightweight testing implementation of a virtual file system.
     33     */
     34    struct StubVFS : public IVFS {
     35
     36        /**
     37         * Creates a stub file system which returns a fixed file content for any file.
     38         * @param dataLength Number of valid consecutive data bytes in 'fileContent'.
     39         * @param fileContent "Data" of all files in the stub file system.
     40         */
     41        StubVFS(size_t dataLength, const u8 fileContent[]) : m_FileSize(dataLength)
     42        {
     43            // As the IVFS is defined to return a smart pointer to the data -
     44            // see LoadFile() - we have to create a copy of the supplied primitive
     45            // memory block, getting rid of the const at the same time.
     46
     47            u8 *dataCopy = new u8[dataLength];
     48            memcpy(dataCopy, fileContent, dataLength);
     49            this->m_FileContent = shared_ptr<u8>(dataCopy);
     50        }
     51
     52        virtual Status Mount(const VfsPath&, const OsPath&, size_t, size_t)
     53        {
     54            throw "Not yet implemented";
     55        }
     56
     57        virtual Status GetFileInfo(const VfsPath&, CFileInfo*) const
     58        {
     59            throw "Not yet implemented";
     60        }
     61
     62        virtual Status GetFilePriority(const VfsPath&, size_t*) const
     63        {
     64            throw "Not yet implemented";
     65        }
     66
     67        virtual Status GetDirectoryEntries(const VfsPath&, CFileInfos*, DirectoryNames*) const
     68        {
     69            throw "Not yet implemented";
     70        }
     71
     72        virtual Status CreateFile(const VfsPath&, const shared_ptr<u8>&, size_t)
     73        {
     74
     75            throw "Not yet implemented";
     76        }
     77
     78        virtual Status ReplaceFile(const VfsPath&, const shared_ptr<u8>&, size_t)
     79        {
     80            throw "Not yet implemented";
     81        }
     82
     83        /**
     84         * Read an entire file into memory.
     85         *
     86         * @param fileContents receives a smart pointer to the contents.
     87         *        CAVEAT: this will be taken from the file cache if the VFS was
     88         *        created with cacheSize != 0 and size < cacheSize. There is no
     89         *        provision for Copy-on-Write, which means that such buffers
     90         *        must not be modified (this is enforced via mprotect).
     91         * @param size receives the size [bytes] of the file contents.
     92         * @return Status.
     93         **/
     94        virtual Status LoadFile(const VfsPath&, shared_ptr<u8>& fileContents, size_t& size)
     95        {
     96            fileContents = this->m_FileContent;
     97            size = this->m_FileSize;
     98            return INFO::OK;
     99        }
     100
     101        virtual std::wstring TextRepresentation() const
     102        {
     103            throw "Not yet implemented";
     104        }
     105
     106        virtual Status GetRealPath(const VfsPath&, OsPath&)
     107        {
     108            throw "Not yet implemented";
     109        }
     110
     111        virtual Status GetDirectoryRealPath(const VfsPath&, OsPath&)
     112        {
     113            throw "Not yet implemented";
     114        }
     115
     116        virtual Status GetVirtualPath(const OsPath&, VfsPath&)
     117        {
     118            throw "Not yet implemented";
     119        }
     120
     121        /**
     122         * remove file from the virtual directory listing and evict its
     123         * data from the cache.
     124         **/
     125        virtual Status RemoveFile(const VfsPath&)
     126        {
     127            throw "Not yet implemented";
     128        }
     129
     130        virtual Status RepopulateDirectory(const VfsPath&)
     131        {
     132            throw "Not yet implemented";
     133        }
     134
     135        virtual void Clear()
     136        {
     137            throw "Not yet implemented";
     138        }
     139
     140    private:
     141
     142        /**
     143         * Data found in all "files" of this file system.
     144         */
     145        shared_ptr<u8> m_FileContent;
     146
     147        /**
     148         * Number of valid elements in 'm_FileContent'
     149         */
     150        const size_t m_FileSize;
     151    };
     152
     153}
     154
     155class TestCVSFFile : public CxxTest::TestSuite
     156{
     157public:
     158
     159    /**
     160     * A file without an UTF8 byte order mark at the start (e.g. a normal
     161     * ASCII file) is loaded properly.
     162     */
     163    void test_decodeutf8_works_without_bom()
     164    {
     165        static const unsigned char testContent[] =
     166        {
     167            'S', 'm', 'i', 'l', 'e', 'y', ' ',
     168            ':', '-', ')'
     169        };
     170
     171        PIVFS pvfs =
     172            PIVFS(new test_CVFSFile::StubVFS(
     173                sizeof(testContent)/sizeof(testContent[0]),
     174                testContent));
     175
     176        CVFSFile testObj;
     177
     178        PSRETURN state = testObj.Load(pvfs, "Testfile.txt");
     179
     180        TS_ASSERT_EQUALS(state, PSRETURN_OK);
     181
     182        CStr decodedStr = testObj.DecodeUTF8();
     183
     184        TS_ASSERT_EQUALS(decodedStr[0], 'S');
     185        TS_ASSERT_EQUALS(decodedStr[2], 'i');
     186        TS_ASSERT_EQUALS(decodedStr.size(),
     187            sizeof(testContent) / sizeof(testContent[0]));
     188    }
     189
     190    /**
     191     * A file with an UTF8 byte order mark at the start is loaded correctly,
     192     * and the byte order mark suppressed on decoding.
     193     */
     194    void test_decodeutf8_accepts_bom()
     195    {
     196        const size_t BOM_LENGTH = 3;
     197        static const unsigned char testContent[] =
     198        {
     199            0xEF, 0xBB, 0xBF, // Byte order mark sequence
     200            'S', 'm', 'i', 'l', 'e', 'y', ' ', // regular text
     201            0xE2, 0x98, 0xBA // smiley (263Ah)
     202        };
     203
     204        PIVFS pvfs =
     205            PIVFS(new test_CVFSFile::StubVFS(
     206                sizeof(testContent)/sizeof(testContent[0]),
     207                testContent));
     208
     209        CVFSFile testObj;
     210
     211        PSRETURN state = testObj.Load(pvfs, "Testfile.txt");
     212
     213        TS_ASSERT_EQUALS(state, PSRETURN_OK);
     214
     215        CStr decodedStr = testObj.DecodeUTF8();
     216
     217        TS_ASSERT_EQUALS(decodedStr[0], 'S');
     218        TS_ASSERT_EQUALS(decodedStr[2], 'i');
     219        TS_ASSERT_EQUALS(decodedStr.size(),
     220            sizeof(testContent) / sizeof(testContent[0]) - BOM_LENGTH);
     221    }
     222
     223};
  • source/scriptinterface/tests/test_ScriptInterface.h

     
    1515 * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
    1616 */
    1717
     18#include "lib/file/vfs/vfs.h"
    1819#include "lib/self_test.h"
    1920
    2021#include "scriptinterface/ScriptInterface.h"
    2122
    2223#include "ps/CLogger.h"
     24#include "ps/Filesystem.h"
    2325
    2426#include <boost/random/linear_congruential.hpp>
    2527
     28/**
     29 * Helper namespace to keep utility classes for ScriptInterface unit tests.
     30 */
     31namespace test_ScriptInterface
     32{
     33
     34    /**
     35     * Lightweight testing implementation of a virtual file system. Intended for unit
     36     * tests only (escape the need to provide actual files on the disk, and reading
     37     * them each time a new test class is launched)
     38     */
     39    struct StubVFS : public IVFS {
     40
     41        /**
     42         * Creates a stub file system which returns a fixed file content for any file.
     43         * @param dataLength Number of valid consecutive data bytes in 'fileContent'.
     44         * @param fileContent "Data" of all files in the stub file system.
     45         */
     46        StubVFS(size_t dataLength, const u8 fileContent[]) : m_FileSize(dataLength)
     47        {
     48            // As the IVFS is defined to return a smart pointer to the data -
     49            // see LoadFile() - we have to create a copy of the supplied primitive
     50            // memory block, getting rid of the const at the same time.
     51
     52            u8 *dataCopy = new u8[dataLength];
     53            memcpy(dataCopy, fileContent, dataLength);
     54            this->m_FileContent = shared_ptr<u8>(dataCopy);
     55        }
     56
     57        virtual Status Mount(const VfsPath&, const OsPath&, size_t, size_t)
     58        {
     59            throw "Not yet implemented";
     60        }
     61
     62        /**
     63         * Retrieve information about a file (similar to POSIX stat).
     64         *
     65         * @param pathname
     66         * @param pfileInfo receives information about the file. Passing NULL
     67         *        suppresses warnings if the file doesn't exist.
     68         *
     69         * @return Always OK (any file is faked to exist, and has the content
     70         *         supplied in the constructor).
     71         **/
     72        virtual Status GetFileInfo(const VfsPath& pathname, CFileInfo* pfileInfo) const
     73        {
     74            if (pfileInfo)
     75            {
     76                *pfileInfo = CFileInfo(pathname, this->m_FileSize, 0);
     77            }
     78            return INFO::OK;
     79        }
     80
     81        virtual Status GetFilePriority(const VfsPath&, size_t*) const
     82        {
     83            throw "Not yet implemented";
     84        }
     85
     86        virtual Status GetDirectoryEntries(const VfsPath&, CFileInfos*, DirectoryNames*) const
     87        {
     88            throw "Not yet implemented";
     89        }
     90
     91        virtual Status CreateFile(const VfsPath&, const shared_ptr<u8>&, size_t)
     92        {
     93
     94            throw "Not yet implemented";
     95        }
     96
     97        virtual Status ReplaceFile(const VfsPath&, const shared_ptr<u8>&, size_t)
     98        {
     99            throw "Not yet implemented";
     100        }
     101
     102        /**
     103         * Read an entire file into memory.
     104         *
     105         * @param fileContents receives a smart pointer to the contents.
     106         *        CAVEAT: this will be taken from the file cache if the VFS was
     107         *        created with cacheSize != 0 and size < cacheSize. There is no
     108         *        provision for Copy-on-Write, which means that such buffers
     109         *        must not be modified (this is enforced via mprotect).
     110         * @param size receives the size [bytes] of the file contents.
     111         * @return Status.
     112         **/
     113        virtual Status LoadFile(const VfsPath&, shared_ptr<u8>& fileContents, size_t& size)
     114        {
     115            fileContents = this->m_FileContent;
     116            size = this->m_FileSize;
     117            return INFO::OK;
     118        }
     119
     120        virtual std::wstring TextRepresentation() const
     121        {
     122            throw "Not yet implemented";
     123        }
     124
     125        virtual Status GetRealPath(const VfsPath&, OsPath&)
     126        {
     127            throw "Not yet implemented";
     128        }
     129
     130        virtual Status GetDirectoryRealPath(const VfsPath&, OsPath&)
     131        {
     132            throw "Not yet implemented";
     133        }
     134
     135        virtual Status GetVirtualPath(const OsPath&, VfsPath&)
     136        {
     137            throw "Not yet implemented";
     138        }
     139
     140        virtual Status RemoveFile(const VfsPath&)
     141        {
     142            throw "Not yet implemented";
     143        }
     144
     145        virtual Status RepopulateDirectory(const VfsPath&)
     146        {
     147            throw "Not yet implemented";
     148        }
     149
     150        virtual void Clear()
     151        {
     152            throw "Not yet implemented";
     153        }
     154
     155    private:
     156
     157        /**
     158         * Data found in all "files" of this file system.
     159         */
     160        shared_ptr<u8> m_FileContent;
     161
     162        /**
     163         * Number of valid elements in 'm_FileContent'
     164         */
     165        const size_t m_FileSize;
     166    };
     167
     168}
     169
    26170class TestScriptInterface : public CxxTest::TestSuite
    27171{
    28172public:
     173
     174    void test_loadglobalscriptfile_accepts_rawascii()
     175    {
     176        PIVFS originalVFS(g_VFS);
     177        ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
     178        TestLogger logger;
     179        // Small calculation in output so we can see whether script was really
     180        // evaluated
     181        static const u8 testScript[] = "warn('The key is: ASC' + (4+7));";
     182
     183        g_VFS = PIVFS(
     184            new test_ScriptInterface::StubVFS(
     185                (sizeof(testScript) / sizeof(testScript[0])) - 1,
     186                testScript));
     187
     188        bool scriptLoaded = script.LoadGlobalScriptFile("test.js");
     189        TS_ASSERT(scriptLoaded);
     190
     191        std::wstring scriptOutput = logger.GetOutput();
     192        TS_ASSERT_WSTR_CONTAINS(scriptOutput, L"The key is: ASC11");
     193        g_VFS = originalVFS;
     194    }
     195
     196    void test_loadglobalscriptfile_accepts_utf8bom()
     197    {
     198        PIVFS originalVFS(g_VFS);
     199        ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
     200        TestLogger logger;
     201        // Small calculation in output so we can see whether script was really
     202        // evaluated
     203        static const u8 testScript[] = "\xEF\xBB\xBFwarn('The key is: ' + (3 * 5));";
     204
     205        g_VFS = PIVFS(
     206            new test_ScriptInterface::StubVFS(
     207                (sizeof(testScript) / sizeof(testScript[0])) - 1,
     208                testScript));
     209
     210        bool scriptLoaded = script.LoadGlobalScriptFile("test.js");
     211        TS_ASSERT(scriptLoaded);
     212
     213        std::wstring scriptOutput = logger.GetOutput();
     214        TS_ASSERT_WSTR_CONTAINS(scriptOutput, L"The key is: 15");
     215        g_VFS = originalVFS;
     216    }
     217
    29218    void test_loadscript_basic()
    30219    {
    31220        ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
     
    126315        TS_ASSERT_EQUALS(d1, d2);
    127316    }
    128317
     318    void test_readjsonfile_accepts_rawascii()
     319    {
     320        PIVFS originalVFS(g_VFS);
     321        static const u8 jsonCode[] = "{ \"encoding\" : \"plain old text\" }";
     322        g_VFS = PIVFS(
     323            new test_ScriptInterface::StubVFS(
     324                sizeof(jsonCode) / sizeof(jsonCode[0]) - 1, // don't include \0 at end
     325                jsonCode));
     326
     327        ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
     328
     329        CScriptValRooted jsonData = script.ReadJSONFile("test.json");
     330        jsval actualObj = jsonData.get();
     331        CStr testValue;
     332        script.GetProperty(actualObj, "encoding", testValue);
     333
     334        TS_ASSERT_EQUALS(testValue, "plain old text");
     335
     336        g_VFS = originalVFS;
     337    }
     338
     339    void test_readjsonfile_accepts_utf8bom()
     340    {
     341        PIVFS originalVFS(g_VFS);
     342        static const u8 jsonCode[] = "\xEF\xBB\xBF{ \"id\" : \"Herakles\" }";
     343        g_VFS = PIVFS(
     344            new test_ScriptInterface::StubVFS(
     345                sizeof(jsonCode) / sizeof(jsonCode[0]) - 1, // don't include \0 at end
     346                jsonCode));
     347
     348        ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
     349
     350        CScriptValRooted jsonData = script.ReadJSONFile("test.json");
     351        jsval actualObj = jsonData.get();
     352        CStr testValue;
     353        script.GetProperty(actualObj, "id", testValue);
     354
     355        TS_ASSERT_EQUALS(testValue, "Herakles");
     356
     357        g_VFS = originalVFS;
     358    }
     359
    129360    void test_json()
    130361    {
    131362        ScriptInterface script("Test", "Test", ScriptInterface::CreateRuntime());
  • source/simulation2/Simulation2.h

     
    263263     */
    264264    std::string GetAIData();
    265265
     266protected:
     267    // Helper for reading JSON files, protected so
     268    // unit tests may access it via inheritance
     269    std::string ReadJSON(VfsPath path);
     270
    266271private:
    267272    CSimulation2Impl* m;
    268273
    269     // Helper for reading JSON files
    270     std::string ReadJSON(VfsPath path);
    271 
    272274    NONCOPYABLE(CSimulation2);
    273275};
    274276
  • source/simulation2/tests/test_ComponentManager.h

     
    6363        man.LoadComponentTypes();
    6464    }
    6565
     66    void test_LoadScriptWithUTF8BOMSucceeds()
     67    {
     68        CSimContext context;
     69        CComponentManager man(context, ScriptInterface::CreateRuntime());
     70
     71        bool isLoaded = man.LoadScript(L"simulation/components/test-bom-utf8.js");
     72        TS_ASSERT(isLoaded);
     73
     74        // The script file loaded before adds a new property "Note" to the Engine
     75        // object, as a simple proof it was executed at all.
     76        ScriptInterface &scripts = man.GetScriptInterface();
     77
     78        CScriptVal engine;
     79        bool hasEngine = scripts.GetProperty(scripts.GetGlobalObject(), "Engine", engine);
     80
     81        TS_ASSERT(hasEngine);
     82
     83        CScriptVal note;
     84        bool hasNote = scripts.GetProperty(engine.get(), "Note", note);
     85        TS_ASSERT(hasNote);
     86
     87        std::wstring noteText = scripts.ToString(note.get(), true);
     88        TS_ASSERT_EQUALS(noteText, L"\"UTF8-BOM was accepted\"");
     89    }
     90
     91    void test_LoadScriptWithoutBOMSucceeds()
     92    {
     93        CSimContext context;
     94        CComponentManager man(context, ScriptInterface::CreateRuntime());
     95
     96        bool isLoaded = man.LoadScript(L"simulation/components/test-bom-none.js");
     97        TS_ASSERT(isLoaded);
     98
     99        // The script file loaded before adds a new property "Note" to the Engine
     100        // object, as a simple proof it was executed at all.
     101        ScriptInterface &scripts = man.GetScriptInterface();
     102
     103        CScriptVal engine;
     104        bool hasEngine = scripts.GetProperty(scripts.GetGlobalObject(), "Engine", engine);
     105
     106        TS_ASSERT(hasEngine);
     107
     108        CScriptVal note;
     109        bool hasNote = scripts.GetProperty(engine.get(), "Note", note);
     110        TS_ASSERT(hasNote);
     111
     112        std::wstring noteText = scripts.ToString(note.get(), true);
     113
     114        TS_ASSERT_EQUALS(noteText, L"\"Script without BOM was accepted\"");
     115    }
     116
    66117    void test_LookupCID()
    67118    {
    68119        CSimContext context;
  • source/simulation2/tests/test_Simulation2.h

     
    2727#include "ps/Filesystem.h"
    2828#include "ps/XML/Xeromyces.h"
    2929
     30/**
     31 * Helper namespace to hold utility classes for testing of 'Simulation2'
     32 * (prevent collision with similary-named stubs for other classes)
     33 */
     34namespace test_Simulation2
     35{
     36
     37    /**
     38     * Lightweight testing implementation of a virtual file system.
     39     *
     40     * While the TestSimulation2 class already has an associated mod (_test.sim),
     41     * testing of the file loader routines via a stub has several advantages:
     42     *  1.  Some routines, such as GetRMSData(), scan a whole directory whose path is
     43     *      hard-coded in the testee. Using a real VFS might cause undesired interferences
     44     *      as more tests are added for a single routine.
     45     *  2.  A pure in-memory VFS is much faster.
     46     *  3.  For UTF-8 tests, control characters (such as a leading BOM) may be expressed
     47     *      directly in the C code, while they might be invisible in some editors when
     48     *      put in real files.
     49     */
     50    struct StubVFS : public IVFS {
     51
     52        /**
     53         * Simulates a file.
     54         */
     55        class StubFile
     56        {
     57        public:
     58
     59            /**
     60             * Initializes a new stub file with fixed content.
     61             * @param filename Name of the file.
     62             * @param dataLength Size of the data held by the fake file.
     63             * @param data Data found in the associated file.
     64             */
     65            StubFile(const char filename[], size_t dataLength, const u8 data[]) :
     66                m_FileName(filename),
     67                m_FileSize(dataLength)
     68            {
     69                u8 *dataCopy = new u8[dataLength];
     70                memcpy(dataCopy, data, dataLength);
     71                this->m_FileContent = shared_ptr<u8>(dataCopy);
     72            }
     73
     74            /**
     75             * Provides the data size of the file.
     76             * @return An info record with file size and a fake date.
     77             */
     78            CFileInfo getInfo() const
     79            {
     80                return CFileInfo(this->getName(), this->m_FileSize, 0);
     81            }
     82
     83            /**
     84             * Retrieves the name of the file.
     85             * @return The filename passed into the constructor.
     86             */
     87            OsPath getName() const
     88            {
     89                return OsPath(this->m_FileName);
     90            }
     91
     92            /**
     93             * Retrieves the data of this file.
     94             * @return A copy of the data passed to the constructor.
     95             */
     96            shared_ptr<u8> getContent() const
     97            {
     98                return this->m_FileContent;
     99            }
     100
     101        private:
     102
     103            /**
     104             * Name of this file.
     105             */
     106            const char *m_FileName;
     107
     108            /**
     109             * Data found in the file represented by this object
     110             */
     111            shared_ptr<u8> m_FileContent;
     112
     113            /**
     114             * Data size of 'm_FileContent'
     115             */
     116            const size_t m_FileSize;
     117        };
     118
     119        /**
     120         * Creates a stub file system which simulates a fixed set of files.
     121         * @param numberOfFiles Number of valid entries in 'files'.
     122         * @param files The files which are provided (no real support for
     123         *              directories, all files are returned on each call to
     124         *              'GetDirectoryEntries').
     125         */
     126        StubVFS(size_t numberOfFiles, const StubFile files[]) :
     127            m_NumberOfFiles(numberOfFiles)
     128        {
     129            this->m_Files = files;
     130        }
     131
     132        virtual Status Mount(const VfsPath&, const OsPath&, size_t, size_t)
     133        {
     134            throw "Not yet implemented";
     135        }
     136
     137        virtual Status GetFileInfo(const VfsPath& path, CFileInfo* info) const
     138        {
     139            for (size_t i = 0; i < this->m_NumberOfFiles; i++)
     140            {
     141                if (this->m_Files[i].getName() == path.Filename())
     142                {
     143                    if (info)
     144                    {
     145                        *info = this->m_Files[i].getInfo();
     146                    }
     147                    return INFO::OK;
     148                }
     149            }
     150            return CODE__PSRETURN_Scripting_LoadFile_OpenFailed;
     151        }
     152
     153        virtual Status GetFilePriority(const VfsPath&, size_t*) const
     154        {
     155            throw "Not yet implemented";
     156        }
     157
     158        /**
     159         * Retrieves a listing of all files held by this file system.
     160         * @param infos Receives additional information about a file.
     161         * @param names Receives the names of all files in the file system.
     162         * @return OK.
     163         */
     164        virtual Status GetDirectoryEntries(const VfsPath&, CFileInfos* infos, DirectoryNames* names) const
     165        {
     166            for (size_t i = 0; i < this->m_NumberOfFiles; i++)
     167            {
     168                if (infos)
     169                {
     170                    infos->push_back(this->m_Files[i].getInfo());
     171                }
     172                if (names)
     173                {
     174                    names->push_back(this->m_Files[i].getName());
     175                }
     176            }
     177            return INFO::OK;
     178        }
     179
     180        virtual Status CreateFile(const VfsPath&, const shared_ptr<u8>&, size_t)
     181        {
     182
     183            throw "Not yet implemented";
     184        }
     185
     186        virtual Status ReplaceFile(const VfsPath&, const shared_ptr<u8>&, size_t)
     187        {
     188            throw "Not yet implemented";
     189        }
     190
     191        /**
     192         * Read an entire file into memory.
     193         *
     194         * @param path Selects the fake file whose content is requested.
     195         * @param fileContents receives a smart pointer to the contents.
     196         *        CAVEAT: this will be taken from the file cache if the VFS was
     197         *        created with cacheSize != 0 and size < cacheSize. There is no
     198         *        provision for Copy-on-Write, which means that such buffers
     199         *        must not be modified (this is enforced via mprotect).
     200         * @param size receives the size [bytes] of the file contents.
     201         * @return OK, if a fake file with given path was selected, or
     202         *         OpenFailed in case the file does not exist.
     203         **/
     204        virtual Status LoadFile(const VfsPath& path, shared_ptr<u8>& fileContents, size_t& size)
     205        {
     206            for (size_t i = 0; i < this->m_NumberOfFiles; i++)
     207            {
     208                if (this->m_Files[i].getName() == path.Filename())
     209                {
     210                    fileContents = this->m_Files[i].getContent();
     211                    size = this->m_Files[i].getInfo().Size();
     212                    return INFO::OK;
     213                }
     214            }
     215            return CODE__PSRETURN_Scripting_LoadFile_OpenFailed;
     216        }
     217
     218        virtual std::wstring TextRepresentation() const
     219        {
     220            throw "Not yet implemented";
     221        }
     222
     223        virtual Status GetRealPath(const VfsPath&, OsPath&)
     224        {
     225            throw "Not yet implemented";
     226        }
     227
     228        virtual Status GetDirectoryRealPath(const VfsPath&, OsPath&)
     229        {
     230            throw "Not yet implemented";
     231        }
     232
     233        virtual Status GetVirtualPath(const OsPath&, VfsPath&)
     234        {
     235            throw "Not yet implemented";
     236        }
     237
     238        /**
     239         * remove file from the virtual directory listing and evict its
     240         * data from the cache.
     241         **/
     242        virtual Status RemoveFile(const VfsPath&)
     243        {
     244            throw "Not yet implemented";
     245        }
     246
     247        virtual Status RepopulateDirectory(const VfsPath&)
     248        {
     249            throw "Not yet implemented";
     250        }
     251
     252        virtual void Clear()
     253        {
     254            throw "Not yet implemented";
     255        }
     256
     257    private:
     258
     259        /**
     260         * Number of valid elements in 'm_Files'.
     261         */
     262        const size_t m_NumberOfFiles;
     263
     264        /**
     265         * The files found in this file system.
     266         */
     267        const StubFile *m_Files;
     268    };
     269
     270}
     271
    30272class TestSimulation2 : public CxxTest::TestSuite
    31273{
    32274    void copyFile(const VfsPath& src, const VfsPath& dst)
     
    39281
    40282    CTerrain m_Terrain;
    41283
     284    /**
     285     * Provides access to CSimulation2::ReadJSON() via inheritance
     286     */
     287    class CSimulation2Hook : public CSimulation2
     288    {
     289    public:
     290        CSimulation2Hook(CUnitManager* unitManager, shared_ptr<ScriptRuntime> rt, CTerrain* terrain) :
     291            CSimulation2(unitManager, rt, terrain)
     292        {
     293        }
     294
     295        std::string ReadJSON(VfsPath path)
     296        {
     297            return CSimulation2::ReadJSON(path);
     298        }
     299    };
     300
     301
    42302public:
    43303    void setUp()
    44304    {
     
    127387        sim.BroadcastMessage(msg);
    128388    }
    129389
     390    /**
     391     * Civ data files shall be loaded with utf8 byte-order-mark suppressed, if present.
     392     */
     393    void test_getcivdata_utf8support()
     394    {
     395        const u8 content1[] = "\xEF\xBB\xBF Asian characters";
     396        const u8 content2[] = "}:-) wikings";
     397        test_Simulation2::StubVFS::StubFile testFiles[] =
     398        {   // Note: "- 1" suppresses the terminating \0 from the "file data"
     399            test_Simulation2::StubVFS::StubFile("asian.json", sizeof(content1)/sizeof(content1[0]) - 1, content1),
     400            test_Simulation2::StubVFS::StubFile("wikings.json", sizeof(content2)/sizeof(content2[0]) - 1, content2)
     401        };
     402
     403        PIVFS originalVFS(g_VFS);
     404        g_VFS = PIVFS(new test_Simulation2::StubVFS(sizeof(testFiles)/sizeof(testFiles[0]), testFiles));
     405
     406        CSimulation2 sim(NULL, ScriptInterface::CreateRuntime(), &m_Terrain);
     407
     408        std::vector<std::string> civData = sim.GetCivData();
     409
     410        size_t numberOfCivs = civData.size();
     411        TS_ASSERT_EQUALS(numberOfCivs, 2);
     412
     413        if (2 == numberOfCivs) // Prevent OutOfRange exception in case of failure
     414        {
     415            TS_ASSERT_EQUALS(civData[0], " Asian characters");
     416            TS_ASSERT_EQUALS(civData[1], "}:-) wikings");
     417        }
     418
     419        g_VFS = originalVFS;
     420    }
     421
     422    /**
     423     * Random map scripts/data shall be loaded with proper utf8 byte-order-mark
     424     * support.
     425     */
     426    void test_getrmsdata_utf8support()
     427    {
     428        const u8 content1[] = "Maritime terrain <.))><";
     429        const u8 content2[] = "\xEF\xBB\xBF Exotic language";
     430        test_Simulation2::StubVFS::StubFile testFiles[] =
     431        {   // Note: "- 1" suppresses the terminating \0 from the "file data"
     432            test_Simulation2::StubVFS::StubFile("script1.json", sizeof(content1)/sizeof(content1[0]) - 1, content1),
     433            test_Simulation2::StubVFS::StubFile("script2.json", sizeof(content2)/sizeof(content2[0]) - 1, content2)
     434        };
     435
     436        PIVFS originalVFS(g_VFS);
     437        g_VFS = PIVFS(new test_Simulation2::StubVFS(sizeof(testFiles)/sizeof(testFiles[0]), testFiles));
     438
     439        CSimulation2 sim(NULL, ScriptInterface::CreateRuntime(), &m_Terrain);
     440
     441        std::vector<std::string> RMSScripts = sim.GetRMSData();
     442
     443        size_t numberOfScripts = RMSScripts.size();
     444        TS_ASSERT_EQUALS(numberOfScripts, 2);
     445
     446        if (2 == numberOfScripts) // Prevent OutOfRange exception in case of failure
     447        {
     448            TS_ASSERT_EQUALS(RMSScripts[0], "Maritime terrain <.))><");
     449            TS_ASSERT_EQUALS(RMSScripts[1], " Exotic language");
     450        }
     451
     452        g_VFS = originalVFS;
     453    }
     454
    130455    void test_hotload_scripts()
    131456    {
    132457        CSimulation2 sim(NULL, ScriptInterface::CreateRuntime(), &m_Terrain);
     
    164489
    165490        TS_ASSERT_OK(sim.ReloadChangedFile(L"simulation/components/hotload/hotload.js"));
    166491    }
     492
     493    void test_readjson_asciifilessupported()
     494    {
     495        const u8 content[] = "\xEF\xBB\xBF{id:'Raw data'}";
     496        test_Simulation2::StubVFS::StubFile testFiles[] =
     497        {   // Note: "- 1" suppresses the terminating \0 from the "file data"
     498            test_Simulation2::StubVFS::StubFile("script.json", sizeof(content)/sizeof(content[0]) - 1, content)
     499        };
     500
     501        PIVFS originalVFS(g_VFS);
     502        g_VFS = PIVFS(new test_Simulation2::StubVFS(sizeof(testFiles)/sizeof(testFiles[0]), testFiles));
     503
     504        CSimulation2Hook sim(NULL, ScriptInterface::CreateRuntime(), &m_Terrain);
     505
     506        std::string result = sim.ReadJSON("script.json");
     507
     508        TS_ASSERT_EQUALS(result, "{id:'Raw data'}");
     509
     510        g_VFS = originalVFS;
     511    }
     512
     513    void test_readjson_utf8bomsupported()
     514    {
     515        const u8 content[] = "\xEF\xBB\xBF{id:'Blah'}";
     516        test_Simulation2::StubVFS::StubFile testFiles[] =
     517        {   // Note: "- 1" suppresses the terminating \0 from the "file data"
     518            test_Simulation2::StubVFS::StubFile("script.json", sizeof(content)/sizeof(content[0]) - 1, content)
     519        };
     520
     521        PIVFS originalVFS(g_VFS);
     522        g_VFS = PIVFS(new test_Simulation2::StubVFS(sizeof(testFiles)/sizeof(testFiles[0]), testFiles));
     523
     524        CSimulation2Hook sim(NULL, ScriptInterface::CreateRuntime(), &m_Terrain);
     525
     526        std::string result = sim.ReadJSON("script.json");
     527
     528        TS_ASSERT_EQUALS(result, "{id:'Blah'}");
     529
     530        g_VFS = originalVFS;
     531    }
     532
    167533};