1 /*
2     SPDX-FileCopyrightText: 2010 Niko Sams <niko.sams@gmail.com>
3     SPDX-FileCopyrightText: 2011 Milian Wolff <mail@milianw.de>
4 
5     SPDX-License-Identifier: LGPL-2.0-only
6 */
7 
8 #include "testfile.h"
9 
10 #include "testproject.h"
11 
12 #include <QTemporaryFile>
13 #include <QElapsedTimer>
14 #include <QTest>
15 
16 #include <language/duchain/duchainlock.h>
17 #include <language/duchain/duchain.h>
18 #include <language/backgroundparser/backgroundparser.h>
19 #include <interfaces/icore.h>
20 #include <interfaces/idocumentcontroller.h>
21 #include <interfaces/ilanguagecontroller.h>
22 #include <project/projectmodel.h>
23 
24 using namespace KDevelop;
25 
26 class KDevelop::TestFilePrivate
27 {
28 public:
TestFilePrivate()29     TestFilePrivate()
30     {
31     }
32 
updateReady(const IndexedString & _url,const ReferencedTopDUContext & _topContext)33     void updateReady(const IndexedString& _url, const ReferencedTopDUContext& _topContext)
34     {
35         Q_ASSERT(_url == url);
36         Q_UNUSED(_url);
37         topContext = _topContext;
38         ready = true;
39     }
40 
init(const QString & fileName,const QString & contents,TestProject * _project)41     void init(const QString& fileName, const QString& contents, TestProject* _project)
42     {
43         file = fileName;
44 
45         setFileContents(contents);
46 
47         QFileInfo info(file);
48         Q_ASSERT(info.exists());
49         Q_ASSERT(info.isFile());
50         url = IndexedString(info.absoluteFilePath());
51 
52         project = _project;
53         if (project) {
54             fileItem.reset(new ProjectFileItem(_project, Path(file), _project->projectItem()));
55         }
56     }
57 
setFileContents(const QString & contents)58     void setFileContents(const QString& contents)
59     {
60         QFile file(this->file);
61         file.open(QIODevice::WriteOnly | QIODevice::Truncate);
62         Q_ASSERT(file.isOpen());
63         Q_ASSERT(file.isWritable());
64         file.write(contents.toUtf8());
65         ready = false;
66     }
67 
68     QString file;
69     QString suffix;
70     bool ready = false;
71     ReferencedTopDUContext topContext;
72     IndexedString url;
73     TestProject* project;
74     QScopedPointer<ProjectFileItem> fileItem;
75     bool keepDUChainData = false;
76 };
77 
TestFile(const QString & contents,const QString & fileExtension,TestProject * project,const QString & dir)78 TestFile::TestFile(const QString& contents, const QString& fileExtension,
79                    TestProject* project, const QString& dir)
80     : d_ptr(new TestFilePrivate())
81 {
82     Q_D(TestFile);
83 
84     d->suffix = QLatin1Char('.') + fileExtension;
85 
86     QTemporaryFile file((!dir.isEmpty() ? dir : QDir::tempPath()) + QLatin1String("/testfile_XXXXXX") + d->suffix);
87     file.setAutoRemove(false);
88     file.open();
89     Q_ASSERT(file.isOpen());
90 
91     d->init(file.fileName(), contents, project);
92 }
93 
TestFile(const QString & contents,const QString & fileExtension,const TestFile * base)94 TestFile::TestFile(const QString& contents, const QString& fileExtension, const TestFile* base)
95     : d_ptr(new TestFilePrivate)
96 {
97     Q_D(TestFile);
98 
99     QString fileName = base->d_func()->file.mid(0, base->d_func()->file.length() - base->d_func()->suffix.length());
100     d->suffix = QLatin1Char('.') + fileExtension;
101     fileName += d->suffix;
102     d->init(fileName, contents, base->d_func()->project);
103 }
104 
TestFile(const QString & contents,const QString & fileExtension,const QString & fileName,KDevelop::TestProject * project,const QString & dir)105 TestFile::TestFile(const QString& contents, const QString& fileExtension, const QString& fileName,
106                    KDevelop::TestProject* project, const QString& dir)
107     : d_ptr(new TestFilePrivate)
108 {
109     Q_D(TestFile);
110 
111     d->suffix = QLatin1Char('.') + fileExtension;
112     const QString file = (!dir.isEmpty() ? dir : QDir::tempPath())
113                     + QLatin1Char('/') + fileName + d->suffix;
114     d->init(file, contents, project);
115 }
116 
117 
~TestFile()118 TestFile::~TestFile()
119 {
120     Q_D(TestFile);
121 
122     if (auto* document = ICore::self()->documentController()->documentForUrl(d->url.toUrl())) {
123         document->close(KDevelop::IDocument::Discard);
124     }
125 
126     auto backgroundParser = ICore::self()->languageController()->backgroundParser();
127     backgroundParser->removeDocument(d->url, this);
128     QTRY_VERIFY(!backgroundParser->parseJobForDocument(d->url));
129 
130     if (d->topContext && !d->keepDUChainData) {
131         DUChainWriteLocker lock;
132         DUChain::self()->removeDocumentChain(d->topContext.data());
133     }
134     QFile::remove(d->file);
135 }
136 
url() const137 IndexedString TestFile::url() const
138 {
139     Q_D(const TestFile);
140 
141     return d->url;
142 }
143 
parse(TopDUContext::Features features,int priority)144 void TestFile::parse(TopDUContext::Features features, int priority)
145 {
146     Q_D(TestFile);
147 
148     d->ready = false;
149     DUChain::self()->updateContextForUrl(d->url, features, this, priority);
150 }
151 
parseAndWait(TopDUContext::Features features,int priority,int timeout)152 bool TestFile::parseAndWait(TopDUContext::Features features, int priority, int timeout)
153 {
154     parse(features, priority);
155     return waitForParsed(timeout);
156 }
157 
waitForParsed(int timeout)158 bool TestFile::waitForParsed(int timeout)
159 {
160     Q_D(TestFile);
161 
162     if (!d->ready) {
163         // optimize: we don't want to wait the usual timeout before parsing documents here
164         ICore::self()->languageController()->backgroundParser()->parseDocuments();
165     }
166     QElapsedTimer t;
167     t.start();
168     while (!d->ready && t.elapsed() < timeout) {
169         QTest::qWait(10);
170     }
171     return d->ready;
172 }
173 
isReady() const174 bool TestFile::isReady() const
175 {
176     Q_D(const TestFile);
177 
178     return d->ready;
179 }
180 
topContext()181 ReferencedTopDUContext TestFile::topContext()
182 {
183     Q_D(TestFile);
184 
185     waitForParsed();
186     return d->topContext;
187 }
188 
setFileContents(const QString & contents)189 void TestFile::setFileContents(const QString& contents)
190 {
191     Q_D(TestFile);
192 
193     d->setFileContents(contents);
194 }
195 
fileContents() const196 QString TestFile::fileContents() const
197 {
198     Q_D(const TestFile);
199 
200     QFile file(d->file);
201     file.open(QIODevice::ReadOnly);
202     Q_ASSERT(file.isOpen());
203     Q_ASSERT(file.isReadable());
204     return QString::fromUtf8(file.readAll());
205 }
206 
setKeepDUChainData(bool keep)207 void TestFile::setKeepDUChainData(bool keep)
208 {
209     Q_D(TestFile);
210 
211     d->keepDUChainData = keep;
212 }
213 
keepDUChainData() const214 bool TestFile::keepDUChainData() const
215 {
216     Q_D(const TestFile);
217 
218     return d->keepDUChainData;
219 }
220 
221 #include "moc_testfile.cpp"
222