mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #2553 from ethereum/extract-docs-tests
Extract examples from documentation and run tests on it
This commit is contained in:
commit
63bf0f68e6
@ -182,6 +182,8 @@ Given the contract:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract Foo {
|
||||
function bar(bytes3[2] xy) {}
|
||||
function baz(uint32 x, bool y) returns (bool r) { r = x > 32 || y; }
|
||||
|
@ -679,6 +679,8 @@ Example:
|
||||
We will follow an example compilation from Solidity to desugared assembly.
|
||||
We consider the runtime bytecode of the following Solidity program::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
function f(uint x) returns (uint y) {
|
||||
y = 1;
|
||||
|
@ -213,6 +213,8 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
|
||||
|
||||
::
|
||||
|
||||
// This will not compile
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
@ -545,9 +547,11 @@ Please ensure you test your fallback function thoroughly to ensure the execution
|
||||
test.call(0xabcdef01); // hash does not exist
|
||||
// results in test.x becoming == 1.
|
||||
|
||||
// The following call will fail, reject the
|
||||
// Ether and return false:
|
||||
test.send(2 ether);
|
||||
// The following will not compile, but even
|
||||
// if someone sends ether to that contract,
|
||||
// the transaction will fail and reject the
|
||||
// Ether.
|
||||
//test.send(2 ether);
|
||||
}
|
||||
}
|
||||
|
||||
@ -773,13 +777,17 @@ seen in the following example::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract owned {
|
||||
function owned() { owner = msg.sender; }
|
||||
address owner;
|
||||
}
|
||||
|
||||
contract mortal is owned {
|
||||
function kill() {
|
||||
if (msg.sender == owner) selfdestruct(owner);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract Base1 is mortal {
|
||||
function kill() { /* do cleanup 1 */ mortal.kill(); }
|
||||
}
|
||||
@ -800,6 +808,11 @@ derived override, but this function will bypass
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract owned {
|
||||
function owned() { owner = msg.sender; }
|
||||
address owner;
|
||||
}
|
||||
|
||||
contract mortal is owned {
|
||||
function kill() {
|
||||
if (msg.sender == owner) selfdestruct(owner);
|
||||
@ -879,6 +892,8 @@ error "Linearization of inheritance graph impossible".
|
||||
|
||||
::
|
||||
|
||||
// This will not compile
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract X {}
|
||||
@ -914,10 +929,16 @@ Contract functions can lack an implementation as in the following example (note
|
||||
function utterance() returns (bytes32);
|
||||
}
|
||||
|
||||
Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts::
|
||||
Such contracts cannot be compiled (even if they contain
|
||||
implemented functions alongside non-implemented functions),
|
||||
but they can be used as base contracts::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract Feline {
|
||||
function utterance() returns (bytes32);
|
||||
}
|
||||
|
||||
contract Cat is Feline {
|
||||
function utterance() returns (bytes32) { return "miaow"; }
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ For example, suppose we want our contract to
|
||||
accept one kind of external calls with two integers, we would write
|
||||
something like::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract Simple {
|
||||
function taker(uint _a, uint _b) {
|
||||
// do something with _a and _b.
|
||||
@ -34,6 +36,8 @@ The output parameters can be declared with the same syntax after the
|
||||
the sum and the product of the two given integers, then we would
|
||||
write::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract Simple {
|
||||
function arithmetics(uint _a, uint _b) returns (uint o_sum, uint o_product) {
|
||||
o_sum = _a + _b;
|
||||
@ -91,6 +95,8 @@ Internal Function Calls
|
||||
Functions of the current contract can be called directly ("internally"), also recursively, as seen in
|
||||
this nonsensical example::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
function g(uint a) returns (uint ret) { return f(); }
|
||||
function f() returns (uint ret) { return g(7) + f(); }
|
||||
@ -116,6 +122,8 @@ all function arguments have to be copied to memory.
|
||||
When calling functions of other contracts, the amount of Wei sent with the call and
|
||||
the gas can be specified with special options ``.value()`` and ``.gas()``, respectively::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract InfoFeed {
|
||||
function info() payable returns (uint ret) { return 42; }
|
||||
}
|
||||
@ -173,7 +181,9 @@ parameters from the function declaration, but can be in arbitrary order.
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
function f(uint key, uint value) { ... }
|
||||
function f(uint key, uint value) {
|
||||
// ...
|
||||
}
|
||||
|
||||
function g() {
|
||||
// named arguments
|
||||
@ -261,6 +271,8 @@ Destructuring Assignments and Returning Multiple Values
|
||||
|
||||
Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose size is a constant at compile-time. Those tuples can be used to return multiple values at the same time and also assign them to multiple variables (or LValues in general) at the same time::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
uint[] data;
|
||||
|
||||
@ -313,6 +325,8 @@ This happens because Solidity inherits its scoping rules from JavaScript.
|
||||
This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block.
|
||||
As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``::
|
||||
|
||||
// This will not compile
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract ScopingErrors {
|
||||
|
@ -48,6 +48,8 @@ non-elementary type, the positions are found by adding an offset of ``keccak256(
|
||||
|
||||
So for the following contract snippet::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
struct s { uint a; uint b; }
|
||||
uint x;
|
||||
|
@ -179,11 +179,13 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
|
||||
}
|
||||
}
|
||||
|
||||
Now someone tricks you into sending ether to the address of this attack wallet:
|
||||
Now someone tricks you into sending ether to the address of this attack wallet::
|
||||
|
||||
::
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
interface TxUserWallet {
|
||||
function transferTo(address dest, uint amount);
|
||||
}
|
||||
|
||||
contract TxAttackWallet {
|
||||
address owner;
|
||||
|
@ -376,7 +376,7 @@ Example that shows how to use internal function types::
|
||||
function (uint, uint) returns (uint) f
|
||||
)
|
||||
internal
|
||||
returns (uint)
|
||||
returns (uint r)
|
||||
{
|
||||
r = self[0];
|
||||
for (uint i = 1; i < self.length; i++) {
|
||||
@ -599,6 +599,8 @@ possible:
|
||||
|
||||
::
|
||||
|
||||
// This will not compile.
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
@ -606,6 +608,7 @@ possible:
|
||||
// The next line creates a type error because uint[3] memory
|
||||
// cannot be converted to uint[] memory.
|
||||
uint[] x = [uint(1), 3, 4];
|
||||
}
|
||||
}
|
||||
|
||||
It is planned to remove this restriction in the future but currently creates
|
||||
@ -812,8 +815,9 @@ for each ``_KeyType``, recursively.
|
||||
}
|
||||
|
||||
contract MappingUser {
|
||||
address contractAddress = 0x42;
|
||||
function f() returns (uint) {
|
||||
return MappingExample(<address>).balances(this);
|
||||
return MappingExample(contractAddress).balances(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# This script reads C++ source files and writes all
|
||||
# This script reads C++ or RST source files and writes all
|
||||
# multi-line strings into individual files.
|
||||
# This can be used to extract the Solidity test cases
|
||||
# into files for e.g. fuzz testing as
|
||||
@ -12,7 +12,7 @@ import os
|
||||
import hashlib
|
||||
from os.path import join
|
||||
|
||||
def extract_cases(path):
|
||||
def extract_test_cases(path):
|
||||
lines = open(path, 'rb').read().splitlines()
|
||||
|
||||
inside = False
|
||||
@ -34,6 +34,44 @@ def extract_cases(path):
|
||||
|
||||
return tests
|
||||
|
||||
# Contract sources are indented by 4 spaces.
|
||||
# Look for `pragma solidity` and abort a line not indented properly.
|
||||
# If the comment `// This will not compile` is above the pragma,
|
||||
# the code is skipped.
|
||||
def extract_docs_cases(path):
|
||||
# Note: this code works, because splitlines() removes empty new lines
|
||||
# and thus even if the empty new lines are missing indentation
|
||||
lines = open(path, 'rb').read().splitlines()
|
||||
|
||||
ignore = False
|
||||
inside = False
|
||||
tests = []
|
||||
|
||||
for l in lines:
|
||||
if inside:
|
||||
# Abort if indentation is missing
|
||||
m = re.search(r'^[^ ]+', l)
|
||||
if m:
|
||||
inside = False
|
||||
else:
|
||||
tests[-1] += l + '\n'
|
||||
else:
|
||||
m = re.search(r'^ // This will not compile', l)
|
||||
if m:
|
||||
ignore = True
|
||||
|
||||
if ignore:
|
||||
# Abort if indentation is missing
|
||||
m = re.search(r'^[^ ]+', l)
|
||||
if m:
|
||||
ignore = False
|
||||
else:
|
||||
m = re.search(r'^ pragma solidity .*[0-9]+\.[0-9]+\.[0-9]+;$', l)
|
||||
if m:
|
||||
inside = True
|
||||
tests += [l]
|
||||
|
||||
return tests
|
||||
|
||||
def write_cases(tests):
|
||||
for test in tests:
|
||||
@ -41,8 +79,17 @@ def write_cases(tests):
|
||||
|
||||
if __name__ == '__main__':
|
||||
path = sys.argv[1]
|
||||
docs = False
|
||||
if len(sys.argv) > 2 and sys.argv[2] == 'docs':
|
||||
docs = True
|
||||
|
||||
for root, dir, files in os.walk(path):
|
||||
for root, subdirs, files in os.walk(path):
|
||||
if '_build' in subdirs:
|
||||
subdirs.remove('_build')
|
||||
for f in files:
|
||||
cases = extract_cases(join(root, f))
|
||||
path = join(root, f)
|
||||
if docs:
|
||||
cases = extract_docs_cases(path)
|
||||
else:
|
||||
cases = extract_test_cases(path)
|
||||
write_cases(cases)
|
||||
|
@ -30,27 +30,6 @@ set -e
|
||||
|
||||
REPO_ROOT="$(dirname "$0")"/..
|
||||
|
||||
echo "Checking that StandardToken.sol, owned.sol and mortal.sol produce bytecode..."
|
||||
output=$("$REPO_ROOT"/build/solc/solc --bin "$REPO_ROOT"/std/*.sol 2>/dev/null | grep "ffff" | wc -l)
|
||||
test "${output//[[:blank:]]/}" = "3"
|
||||
|
||||
echo "Compiling various other contracts and libraries..."
|
||||
(
|
||||
cd "$REPO_ROOT"/test/compilationTests/
|
||||
for dir in *
|
||||
do
|
||||
if [ "$dir" != "README.md" ]
|
||||
then
|
||||
echo " - $dir"
|
||||
cd "$dir"
|
||||
../../../build/solc/solc --optimize \
|
||||
--combined-json abi,asm,ast,bin,bin-runtime,clone-bin,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc \
|
||||
*.sol */*.sol > /dev/null 2>&1
|
||||
cd ..
|
||||
fi
|
||||
done
|
||||
)
|
||||
|
||||
echo "Running commandline tests..."
|
||||
"$REPO_ROOT/test/cmdlineTests.sh"
|
||||
|
||||
|
@ -28,26 +28,86 @@
|
||||
|
||||
set -e
|
||||
|
||||
REPO_ROOT="$(dirname "$0")"/..
|
||||
REPO_ROOT=$(cd $(dirname "$0")/.. && pwd)
|
||||
echo $REPO_ROOT
|
||||
SOLC="$REPO_ROOT/build/solc/solc"
|
||||
|
||||
echo "Checking that the bug list is up to date..."
|
||||
"$REPO_ROOT"/scripts/update_bugs_by_version.py
|
||||
|
||||
echo "Compiling all files in std and examples..."
|
||||
echo "Checking that StandardToken.sol, owned.sol and mortal.sol produce bytecode..."
|
||||
output=$("$REPO_ROOT"/build/solc/solc --bin "$REPO_ROOT"/std/*.sol 2>/dev/null | grep "ffff" | wc -l)
|
||||
test "${output//[[:blank:]]/}" = "3"
|
||||
|
||||
for f in "$REPO_ROOT"/std/*.sol
|
||||
do
|
||||
echo "Compiling $f..."
|
||||
function compileFull()
|
||||
{
|
||||
files="$*"
|
||||
set +e
|
||||
output=$("$SOLC" "$f" 2>&1)
|
||||
"$SOLC" --optimize \
|
||||
--combined-json abi,asm,ast,bin,bin-runtime,clone-bin,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc \
|
||||
$files >/dev/null 2>&1
|
||||
failed=$?
|
||||
set -e
|
||||
if [ $failed -ne 0 ]
|
||||
then
|
||||
echo "Compilation failed on:"
|
||||
cat $files
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
function compileWithoutWarning()
|
||||
{
|
||||
files="$*"
|
||||
set +e
|
||||
output=$("$SOLC" $files 2>&1)
|
||||
failed=$?
|
||||
# Remove the pre-release warning from the compiler output
|
||||
output=$(echo "$output" | grep -v 'pre-release')
|
||||
echo "$output"
|
||||
set -e
|
||||
test -z "$output" -a "$failed" -eq 0
|
||||
}
|
||||
|
||||
echo "Compiling various other contracts and libraries..."
|
||||
(
|
||||
cd "$REPO_ROOT"/test/compilationTests/
|
||||
for dir in *
|
||||
do
|
||||
if [ "$dir" != "README.md" ]
|
||||
then
|
||||
echo " - $dir"
|
||||
cd "$dir"
|
||||
compileFull *.sol */*.sol
|
||||
cd ..
|
||||
fi
|
||||
done
|
||||
)
|
||||
|
||||
echo "Compiling all files in std and examples..."
|
||||
|
||||
for f in "$REPO_ROOT"/std/*.sol
|
||||
do
|
||||
echo "$f"
|
||||
compileWithoutWarning "$f"
|
||||
done
|
||||
|
||||
echo "Compiling all examples from the documentation..."
|
||||
TMPDIR=$(mktemp -d)
|
||||
(
|
||||
set -e
|
||||
cd "$REPO_ROOT"
|
||||
REPO_ROOT=$(pwd) # make it absolute
|
||||
cd "$TMPDIR"
|
||||
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
|
||||
for f in *.sol
|
||||
do
|
||||
echo "$f"
|
||||
compileFull "$TMPDIR/$f"
|
||||
done
|
||||
)
|
||||
rm -rf "$TMPDIR"
|
||||
echo "Done."
|
||||
|
||||
echo "Testing library checksum..."
|
||||
echo '' | "$SOLC" --link --libraries a:0x90f20564390eAe531E810af625A22f51385Cd222
|
||||
@ -77,6 +137,7 @@ TMPDIR=$(mktemp -d)
|
||||
REPO_ROOT=$(pwd) # make it absolute
|
||||
cd "$TMPDIR"
|
||||
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/
|
||||
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
|
||||
for f in *.sol
|
||||
do
|
||||
set +e
|
||||
|
Loading…
Reference in New Issue
Block a user