1 /*
2 	This file is part of solidity.
3 
4 	solidity 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 3 of the License, or
7 	(at your option) any later version.
8 
9 	solidity 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 solidity.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 /**
18  * @date 2017
19  * Unit tests for the metadata output.
20  */
21 
22 #include <test/Metadata.h>
23 #include <test/Common.h>
24 #include <libsolidity/interface/CompilerStack.h>
25 #include <libsolidity/interface/Version.h>
26 #include <libsolutil/SwarmHash.h>
27 #include <libsolutil/IpfsHash.h>
28 #include <libsolutil/JSON.h>
29 
30 #include <boost/test/unit_test.hpp>
31 
32 using namespace std;
33 
34 namespace solidity::frontend::test
35 {
36 
37 namespace
38 {
39 
requireParsedCBORMetadata(bytes const & _bytecode,CompilerStack::MetadataFormat _metadataFormat)40 map<string, string> requireParsedCBORMetadata(bytes const& _bytecode, CompilerStack::MetadataFormat _metadataFormat)
41 {
42 	bytes cborMetadata = solidity::test::onlyMetadata(_bytecode);
43 	if (_metadataFormat != CompilerStack::MetadataFormat::NoMetadata)
44 	{
45 		BOOST_REQUIRE(!cborMetadata.empty());
46 		std::optional<map<string, string>> tmp = solidity::test::parseCBORMetadata(cborMetadata);
47 		BOOST_REQUIRE(tmp);
48 		return *tmp;
49 	}
50 	BOOST_REQUIRE(cborMetadata.empty());
51 	return {};
52 }
53 
compileAndCheckLicenseMetadata(string const & _contractName,char const * _sourceCode)54 optional<string> compileAndCheckLicenseMetadata(string const& _contractName, char const* _sourceCode)
55 {
56 	CompilerStack compilerStack;
57 	compilerStack.setSources({{"A.sol", std::string(_sourceCode)}});
58 	BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
59 
60 	std::string const& serialisedMetadata = compilerStack.metadata(_contractName);
61 	Json::Value metadata;
62 	BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata));
63 	BOOST_CHECK(solidity::test::isValidMetadata(metadata));
64 
65 	BOOST_CHECK_EQUAL(metadata["sources"].size(), 1);
66 	BOOST_REQUIRE(metadata["sources"].isMember("A.sol"));
67 
68 	if (metadata["sources"]["A.sol"].isMember("license"))
69 	{
70 		BOOST_REQUIRE(metadata["sources"]["A.sol"]["license"].isString());
71 		return metadata["sources"]["A.sol"]["license"].asString();
72 	}
73 	else
74 		return nullopt;
75 }
76 
77 }
78 
79 BOOST_AUTO_TEST_SUITE(Metadata)
80 
BOOST_AUTO_TEST_CASE(metadata_stamp)81 BOOST_AUTO_TEST_CASE(metadata_stamp)
82 {
83 	// Check that the metadata stamp is at the end of the runtime bytecode.
84 	char const* sourceCode = R"(
85 		pragma solidity >=0.0;
86 		pragma experimental __testOnlyAnalysis;
87 		contract test {
88 			function g(function(uint) external returns (uint) x) public {}
89 		}
90 	)";
91 	for (auto metadataFormat: std::set<CompilerStack::MetadataFormat>{
92 		CompilerStack::MetadataFormat::NoMetadata,
93 		CompilerStack::MetadataFormat::WithReleaseVersionTag,
94 		CompilerStack::MetadataFormat::WithPrereleaseVersionTag
95 	})
96 		for (auto metadataHash: set<CompilerStack::MetadataHash>{
97 			CompilerStack::MetadataHash::IPFS,
98 			CompilerStack::MetadataHash::Bzzr1,
99 			CompilerStack::MetadataHash::None
100 		})
101 		{
102 			CompilerStack compilerStack;
103 			compilerStack.setMetadataFormat(metadataFormat);
104 			compilerStack.setSources({{"", std::string(sourceCode)}});
105 			compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
106 			compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize);
107 			compilerStack.setMetadataHash(metadataHash);
108 			BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
109 			bytes const& bytecode = compilerStack.runtimeObject("test").bytecode;
110 			std::string const& metadata = compilerStack.metadata("test");
111 			BOOST_CHECK(solidity::test::isValidMetadata(metadata));
112 
113 			auto const cborMetadata = requireParsedCBORMetadata(bytecode, metadataFormat);
114 			if (metadataHash == CompilerStack::MetadataHash::None)
115 				BOOST_CHECK(cborMetadata.size() == (metadataFormat == CompilerStack::MetadataFormat::NoMetadata ? 0 : 1));
116 			else
117 			{
118 				bytes hash;
119 				string hashMethod;
120 				if (metadataHash == CompilerStack::MetadataHash::IPFS)
121 				{
122 					hash = util::ipfsHash(metadata);
123 					BOOST_REQUIRE(hash.size() == 34);
124 					hashMethod = "ipfs";
125 				}
126 				else
127 				{
128 					hash = util::bzzr1Hash(metadata).asBytes();
129 					BOOST_REQUIRE(hash.size() == 32);
130 					hashMethod = "bzzr1";
131 				}
132 
133 				if (metadataFormat != CompilerStack::MetadataFormat::NoMetadata)
134 				{
135 					BOOST_CHECK(cborMetadata.size() == 2);
136 					BOOST_CHECK(cborMetadata.count(hashMethod) == 1);
137 					BOOST_CHECK(cborMetadata.at(hashMethod) == util::toHex(hash));
138 				}
139 			}
140 
141 			if (metadataFormat == CompilerStack::MetadataFormat::NoMetadata)
142 				BOOST_CHECK(cborMetadata.count("solc") == 0);
143 			else
144 			{
145 				BOOST_CHECK(cborMetadata.count("solc") == 1);
146 				if (metadataFormat == CompilerStack::MetadataFormat::WithReleaseVersionTag)
147 					BOOST_CHECK(cborMetadata.at("solc") == util::toHex(VersionCompactBytes));
148 				else
149 					BOOST_CHECK(cborMetadata.at("solc") == VersionStringStrict);
150 			}
151 		}
152 }
153 
BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)154 BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)
155 {
156 	// Check that the metadata stamp is at the end of the runtime bytecode.
157 	char const* sourceCode = R"(
158 		pragma solidity >=0.0;
159 		pragma experimental __test;
160 		contract test {
161 			function g(function(uint) external returns (uint) x) public {}
162 		}
163 	)";
164 	for (auto metadataFormat: std::set<CompilerStack::MetadataFormat>{
165 			CompilerStack::MetadataFormat::NoMetadata,
166 			CompilerStack::MetadataFormat::WithReleaseVersionTag,
167 			CompilerStack::MetadataFormat::WithPrereleaseVersionTag
168 	})
169 		for (auto metadataHash: set<CompilerStack::MetadataHash>{
170 			CompilerStack::MetadataHash::IPFS,
171 			CompilerStack::MetadataHash::Bzzr1,
172 			CompilerStack::MetadataHash::None
173 		})
174 		{
175 			CompilerStack compilerStack;
176 			compilerStack.setMetadataFormat(metadataFormat);
177 			compilerStack.setSources({{"", std::string(sourceCode)}});
178 			compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
179 			compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize);
180 			compilerStack.setMetadataHash(metadataHash);
181 			BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
182 			bytes const& bytecode = compilerStack.runtimeObject("test").bytecode;
183 			std::string const& metadata = compilerStack.metadata("test");
184 			BOOST_CHECK(solidity::test::isValidMetadata(metadata));
185 
186 			auto const cborMetadata = requireParsedCBORMetadata(bytecode, metadataFormat);
187 			if (metadataHash == CompilerStack::MetadataHash::None)
188 				BOOST_CHECK(cborMetadata.size() == (metadataFormat == CompilerStack::MetadataFormat::NoMetadata ? 0 : 2));
189 			else
190 			{
191 				bytes hash;
192 				string hashMethod;
193 				if (metadataHash == CompilerStack::MetadataHash::IPFS)
194 				{
195 					hash = util::ipfsHash(metadata);
196 					BOOST_REQUIRE(hash.size() == 34);
197 					hashMethod = "ipfs";
198 				}
199 				else
200 				{
201 					hash = util::bzzr1Hash(metadata).asBytes();
202 					BOOST_REQUIRE(hash.size() == 32);
203 					hashMethod = "bzzr1";
204 				}
205 
206 				if (metadataFormat != CompilerStack::MetadataFormat::NoMetadata)
207 				{
208 					BOOST_CHECK(cborMetadata.size() == 3);
209 					BOOST_CHECK(cborMetadata.count(hashMethod) == 1);
210 					BOOST_CHECK(cborMetadata.at(hashMethod) == util::toHex(hash));
211 				}
212 			}
213 
214 			if (metadataFormat == CompilerStack::MetadataFormat::NoMetadata)
215 				BOOST_CHECK(cborMetadata.count("solc") == 0);
216 			else
217 			{
218 				BOOST_CHECK(cborMetadata.count("solc") == 1);
219 				if (metadataFormat == CompilerStack::MetadataFormat::WithReleaseVersionTag)
220 					BOOST_CHECK(cborMetadata.at("solc") == util::toHex(VersionCompactBytes));
221 				else
222 					BOOST_CHECK(cborMetadata.at("solc") == VersionStringStrict);
223 				BOOST_CHECK(cborMetadata.count("experimental") == 1);
224 				BOOST_CHECK(cborMetadata.at("experimental") == "true");
225 			}
226 		}
227 }
228 
BOOST_AUTO_TEST_CASE(metadata_relevant_sources)229 BOOST_AUTO_TEST_CASE(metadata_relevant_sources)
230 {
231 	CompilerStack compilerStack;
232 	char const* sourceCodeA = R"(
233 		pragma solidity >=0.0;
234 		contract A {
235 			function g(function(uint) external returns (uint) x) public {}
236 		}
237 	)";
238 	char const* sourceCodeB = R"(
239 		pragma solidity >=0.0;
240 		contract B {
241 			function g(function(uint) external returns (uint) x) public {}
242 		}
243 	)";
244 	compilerStack.setSources({
245 		{"A", std::string(sourceCodeA)},
246 		{"B", std::string(sourceCodeB)},
247 	});
248 	compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
249 	compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize);
250 	BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
251 
252 	std::string const& serialisedMetadata = compilerStack.metadata("A");
253 	Json::Value metadata;
254 	BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata));
255 	BOOST_CHECK(solidity::test::isValidMetadata(metadata));
256 
257 	BOOST_CHECK_EQUAL(metadata["sources"].size(), 1);
258 	BOOST_CHECK(metadata["sources"].isMember("A"));
259 }
260 
BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports)261 BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports)
262 {
263 	CompilerStack compilerStack;
264 	char const* sourceCodeA = R"(
265 		pragma solidity >=0.0;
266 		contract A {
267 			function g(function(uint) external returns (uint) x) public virtual {}
268 		}
269 	)";
270 	char const* sourceCodeB = R"(
271 		pragma solidity >=0.0;
272 		import "./A";
273 		contract B is A {
274 			function g(function(uint) external returns (uint) x) public virtual override {}
275 		}
276 	)";
277 	char const* sourceCodeC = R"(
278 		pragma solidity >=0.0;
279 		import "./B";
280 		contract C is B {
281 			function g(function(uint) external returns (uint) x) public override {}
282 		}
283 	)";
284 	compilerStack.setSources({
285 		{"A", std::string(sourceCodeA)},
286 		{"B", std::string(sourceCodeB)},
287 		{"C", std::string(sourceCodeC)}
288 	});
289 	compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
290 	compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize);
291 	BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
292 
293 	std::string const& serialisedMetadata = compilerStack.metadata("C");
294 	Json::Value metadata;
295 	BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata));
296 	BOOST_CHECK(solidity::test::isValidMetadata(metadata));
297 
298 	BOOST_CHECK_EQUAL(metadata["sources"].size(), 3);
299 	BOOST_CHECK(metadata["sources"].isMember("A"));
300 	BOOST_CHECK(metadata["sources"].isMember("B"));
301 	BOOST_CHECK(metadata["sources"].isMember("C"));
302 }
303 
BOOST_AUTO_TEST_CASE(metadata_useLiteralContent)304 BOOST_AUTO_TEST_CASE(metadata_useLiteralContent)
305 {
306 	// Check that the metadata contains "useLiteralContent"
307 	char const* sourceCode = R"(
308 		pragma solidity >=0.0;
309 		contract test {
310 		}
311 	)";
312 
313 	auto check = [](char const* _src, bool _literal)
314 	{
315 		CompilerStack compilerStack;
316 		compilerStack.setSources({{"", std::string(_src)}});
317 		compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
318 		compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize);
319 		compilerStack.useMetadataLiteralSources(_literal);
320 		BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
321 		string metadata_str = compilerStack.metadata("test");
322 		Json::Value metadata;
323 		BOOST_REQUIRE(util::jsonParseStrict(metadata_str, metadata));
324 		BOOST_CHECK(solidity::test::isValidMetadata(metadata));
325 		BOOST_CHECK(metadata.isMember("settings"));
326 		BOOST_CHECK(metadata["settings"].isMember("metadata"));
327 		BOOST_CHECK(metadata["settings"]["metadata"].isMember("bytecodeHash"));
328 		if (_literal)
329 		{
330 			BOOST_CHECK(metadata["settings"]["metadata"].isMember("useLiteralContent"));
331 			BOOST_CHECK(metadata["settings"]["metadata"]["useLiteralContent"].asBool());
332 		}
333 	};
334 
335 	check(sourceCode, true);
336 	check(sourceCode, false);
337 }
338 
BOOST_AUTO_TEST_CASE(metadata_viair)339 BOOST_AUTO_TEST_CASE(metadata_viair)
340 {
341 	char const* sourceCode = R"(
342 		pragma solidity >=0.0;
343 		contract test {
344 		}
345 	)";
346 
347 	auto check = [](char const* _src, bool _viaIR)
348 	{
349 		CompilerStack compilerStack;
350 		compilerStack.setSources({{"", std::string(_src)}});
351 		compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion());
352 		compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize);
353 		compilerStack.setViaIR(_viaIR);
354 		BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
355 
356 		Json::Value metadata;
357 		BOOST_REQUIRE(util::jsonParseStrict(compilerStack.metadata("test"), metadata));
358 		BOOST_CHECK(solidity::test::isValidMetadata(metadata));
359 		BOOST_CHECK(metadata.isMember("settings"));
360 		if (_viaIR)
361 		{
362 			BOOST_CHECK(metadata["settings"].isMember("viaIR"));
363 			BOOST_CHECK(metadata["settings"]["viaIR"].asBool());
364 		}
365 		else
366 			BOOST_CHECK(!metadata["settings"].isMember("viaIR"));
367 
368 		BOOST_CHECK(compilerStack.cborMetadata("test") == compilerStack.cborMetadata("test", _viaIR));
369 		BOOST_CHECK(compilerStack.cborMetadata("test") != compilerStack.cborMetadata("test", !_viaIR));
370 
371 		map<string, string> const parsedCBORMetadata = requireParsedCBORMetadata(
372 			compilerStack.runtimeObject("test").bytecode,
373 			CompilerStack::MetadataFormat::WithReleaseVersionTag
374 		);
375 
376 		if (_viaIR)
377 		{
378 			BOOST_CHECK(parsedCBORMetadata.count("experimental") == 1);
379 			BOOST_CHECK(parsedCBORMetadata.at("experimental") == "true");
380 		}
381 		else
382 			BOOST_CHECK(parsedCBORMetadata.count("experimental") == 0);
383 	};
384 
385 	check(sourceCode, true);
386 	check(sourceCode, false);
387 }
388 
BOOST_AUTO_TEST_CASE(metadata_revert_strings)389 BOOST_AUTO_TEST_CASE(metadata_revert_strings)
390 {
391 	CompilerStack compilerStack;
392 	char const* sourceCodeA = R"(
393 		pragma solidity >=0.0;
394 		contract A {
395 		}
396 	)";
397 	compilerStack.setSources({{"A", std::string(sourceCodeA)}});
398 	compilerStack.setRevertStringBehaviour(RevertStrings::Strip);
399 	BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
400 
401 	std::string const& serialisedMetadata = compilerStack.metadata("A");
402 	Json::Value metadata;
403 	BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata));
404 	BOOST_CHECK(solidity::test::isValidMetadata(metadata));
405 
406 	BOOST_CHECK_EQUAL(metadata["settings"]["debug"]["revertStrings"], "strip");
407 }
408 
BOOST_AUTO_TEST_CASE(metadata_license_missing)409 BOOST_AUTO_TEST_CASE(metadata_license_missing)
410 {
411 	char const* sourceCode = R"(
412 		pragma solidity >=0.0;
413 		contract C {
414 		}
415 	)";
416 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt);
417 }
418 
BOOST_AUTO_TEST_CASE(metadata_license_gpl3)419 BOOST_AUTO_TEST_CASE(metadata_license_gpl3)
420 {
421 	// Can't use a raw string here due to the stylechecker.
422 	char const* sourceCode =
423 		"// NOTE: we also add trailing whitespace after the license, to see it is trimmed.\n"
424 		"// SPDX-License-Identifier: GPL-3.0    \n"
425 		"pragma solidity >=0.0;\n"
426 		"contract C {\n"
427 		"}\n";
428 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
429 }
430 
BOOST_AUTO_TEST_CASE(metadata_license_whitespace_before_spdx)431 BOOST_AUTO_TEST_CASE(metadata_license_whitespace_before_spdx)
432 {
433 	char const* sourceCode = R"(
434 		//     SPDX-License-Identifier: GPL-3.0
435 		contract C {}
436 	)";
437 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
438 }
439 
BOOST_AUTO_TEST_CASE(metadata_license_whitespace_after_colon)440 BOOST_AUTO_TEST_CASE(metadata_license_whitespace_after_colon)
441 {
442 	char const* sourceCode = R"(
443 		// SPDX-License-Identifier:    GPL-3.0
444 		contract C {}
445 	)";
446 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
447 }
448 
BOOST_AUTO_TEST_CASE(metadata_license_gpl3_or_apache2)449 BOOST_AUTO_TEST_CASE(metadata_license_gpl3_or_apache2)
450 {
451 	char const* sourceCode = R"(
452 		// SPDX-License-Identifier: GPL-3.0 OR Apache-2.0
453 		pragma solidity >=0.0;
454 		contract C {
455 		}
456 	)";
457 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0 OR Apache-2.0");
458 }
459 
BOOST_AUTO_TEST_CASE(metadata_license_bidi_marks)460 BOOST_AUTO_TEST_CASE(metadata_license_bidi_marks)
461 {
462 	char const* sourceCode =
463 		"// \xE2\x80\xAE""0.3-LPG :reifitnedI-esneciL-XDPS\xE2\x80\xAC\n"
464 		"// NOTE: The text above is reversed using Unicode directional marks. In raw form it would look like this:\n"
465 		"// <LRO>0.3-LPG :reifitnedI-esneciL-XDPS<PDF>\n"
466 		"contract C {}\n";
467 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt);
468 }
469 
BOOST_AUTO_TEST_CASE(metadata_license_bottom)470 BOOST_AUTO_TEST_CASE(metadata_license_bottom)
471 {
472 	char const* sourceCode = R"(
473 		contract C {}
474 		// SPDX-License-Identifier: GPL-3.0
475 	)";
476 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
477 }
478 
BOOST_AUTO_TEST_CASE(metadata_cr_endings)479 BOOST_AUTO_TEST_CASE(metadata_cr_endings)
480 {
481 	char const* sourceCode =
482 		"// SPDX-License-Identifier: GPL-3.0\r"
483 		"contract C {}\r";
484 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
485 }
486 
BOOST_AUTO_TEST_CASE(metadata_crlf_endings)487 BOOST_AUTO_TEST_CASE(metadata_crlf_endings)
488 {
489 	char const* sourceCode =
490 		"// SPDX-License-Identifier: GPL-3.0\r\n"
491 		"contract C {}\r\n";
492 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
493 }
494 
BOOST_AUTO_TEST_CASE(metadata_license_in_string)495 BOOST_AUTO_TEST_CASE(metadata_license_in_string)
496 {
497 	char const* sourceCode = R"(
498 		contract C {
499 			bytes license = "// SPDX-License-Identifier: GPL-3.0";
500 		}
501 	)";
502 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt);
503 }
504 
BOOST_AUTO_TEST_CASE(metadata_license_in_contract)505 BOOST_AUTO_TEST_CASE(metadata_license_in_contract)
506 {
507 	char const* sourceCode = R"(
508 		contract C {
509 		// SPDX-License-Identifier: GPL-3.0
510 		}
511 	)";
512 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt);
513 }
514 
BOOST_AUTO_TEST_CASE(metadata_license_missing_colon)515 BOOST_AUTO_TEST_CASE(metadata_license_missing_colon)
516 {
517 	char const* sourceCode = R"(
518 		// SPDX-License-Identifier GPL-3.0
519 		contract C {}
520 	)";
521 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == nullopt);
522 }
523 
BOOST_AUTO_TEST_CASE(metadata_license_multiline)524 BOOST_AUTO_TEST_CASE(metadata_license_multiline)
525 {
526 	char const* sourceCode = R"(
527 		/* SPDX-License-Identifier: GPL-3.0 */
528 		contract C {}
529 	)";
530 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
531 }
532 
BOOST_AUTO_TEST_CASE(metadata_license_natspec)533 BOOST_AUTO_TEST_CASE(metadata_license_natspec)
534 {
535 	char const* sourceCode = R"(
536 		/// SPDX-License-Identifier: GPL-3.0
537 		contract C {}
538 	)";
539 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
540 }
541 
BOOST_AUTO_TEST_CASE(metadata_license_natspec_multiline)542 BOOST_AUTO_TEST_CASE(metadata_license_natspec_multiline)
543 {
544 	char const* sourceCode = R"(
545 		/** SPDX-License-Identifier: GPL-3.0 */
546 		contract C {}
547 	)";
548 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
549 }
550 
BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace_multiline)551 BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace_multiline)
552 {
553 	char const* sourceCode = R"(
554 		/*SPDX-License-Identifier:GPL-3.0*/
555 		contract C {}
556 	)";
557 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
558 }
559 
BOOST_AUTO_TEST_CASE(metadata_license_nonempty_line)560 BOOST_AUTO_TEST_CASE(metadata_license_nonempty_line)
561 {
562 	char const* sourceCode = R"(
563 		pragma solidity >= 0.0; // SPDX-License-Identifier: GPL-3.0
564 		contract C {}
565 	)";
566 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
567 }
568 
BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace)569 BOOST_AUTO_TEST_CASE(metadata_license_no_whitespace)
570 {
571 	char const* sourceCode = R"(
572 		//SPDX-License-Identifier:GPL-3.0
573 		contract C {}
574 	)";
575 	BOOST_CHECK(compileAndCheckLicenseMetadata("C", sourceCode) == "GPL-3.0");
576 }
577 
578 BOOST_AUTO_TEST_SUITE_END()
579 
580 }
581