aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2017-07-14 04:16:49 +0800
committerGitHub <noreply@github.com>2017-07-14 04:16:49 +0800
commit63bf0f68e6d232eccf6d64ca2bba5b39e108ea41 (patch)
tree2e6468e73cc51830328f4f323b2d84b406b0399a
parentb5da5f6e42c9d60206d5045ace471c7b5839ed30 (diff)
parenta8d78bb7675f81dd992a588582501d7db6d578d2 (diff)
downloaddexon-solidity-63bf0f68e6d232eccf6d64ca2bba5b39e108ea41.tar
dexon-solidity-63bf0f68e6d232eccf6d64ca2bba5b39e108ea41.tar.gz
dexon-solidity-63bf0f68e6d232eccf6d64ca2bba5b39e108ea41.tar.bz2
dexon-solidity-63bf0f68e6d232eccf6d64ca2bba5b39e108ea41.tar.lz
dexon-solidity-63bf0f68e6d232eccf6d64ca2bba5b39e108ea41.tar.xz
dexon-solidity-63bf0f68e6d232eccf6d64ca2bba5b39e108ea41.tar.zst
dexon-solidity-63bf0f68e6d232eccf6d64ca2bba5b39e108ea41.zip
Merge pull request #2553 from ethereum/extract-docs-tests
Extract examples from documentation and run tests on it
-rw-r--r--docs/abi-spec.rst2
-rw-r--r--docs/assembly.rst2
-rw-r--r--docs/contracts.rst31
-rw-r--r--docs/control-structures.rst16
-rw-r--r--docs/miscellaneous.rst2
-rw-r--r--docs/security-considerations.rst8
-rw-r--r--docs/types.rst8
-rwxr-xr-xscripts/isolate_tests.py55
-rwxr-xr-xscripts/tests.sh21
-rwxr-xr-xtest/cmdlineTests.sh73
10 files changed, 176 insertions, 42 deletions
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
index d915973d..82c52159 100644
--- a/docs/abi-spec.rst
+++ b/docs/abi-spec.rst
@@ -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; }
diff --git a/docs/assembly.rst b/docs/assembly.rst
index 0a120644..37222faf 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -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;
diff --git a/docs/contracts.rst b/docs/contracts.rst
index e9ea1b3b..3c9769ff 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -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"; }
}
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index 03787c20..f4c0b776 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -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 {
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 182de33a..1fcdb2fc 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -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;
diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst
index 33c613d8..658c12b2 100644
--- a/docs/security-considerations.rst
+++ b/docs/security-considerations.rst
@@ -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;
diff --git a/docs/types.rst b/docs/types.rst
index 67549499..b9ecd083 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -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);
}
}
diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py
index a1d1c75c..cfaef210 100755
--- a/scripts/isolate_tests.py
+++ b/scripts/isolate_tests.py
@@ -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)
diff --git a/scripts/tests.sh b/scripts/tests.sh
index 64b4121f..5d7eb0e1 100755
--- a/scripts/tests.sh
+++ b/scripts/tests.sh
@@ -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"
diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh
index 4074ce55..eb5c714d 100755
--- a/test/cmdlineTests.sh
+++ b/test/cmdlineTests.sh
@@ -28,27 +28,87 @@
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
+ "$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" "$f" 2>&1)
+ 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
! echo '' | "$SOLC" --link --libraries a:0x80f20564390eAe531E810af625A22f51385Cd222 2>/dev/null
@@ -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